typescript - Funktion med parametrar av unionstyp misslyckas, men alla möjliga typkombinationer fungerar - hur kommer det sig?
const add = (x:string|number) => x+x
ger ett TS-fel:
Operator '+' cannot be applied to types 'string | number' and 'string | number'.ts(2365)
Men alla möjliga typkombinationer
const add = (x:string) => x+x
const add = (x:number) => x+x
passera typkontroll som giltig. Hur kommer det sig?
Svar #1
Antagligen tror du att att lägga till två string | number-operander bör lyckas och resultera i en string | number-utgång. Eller mer generellt att den binära +-operatorn bör distribueras över unioner i typerna av dess operander.
Min forskning indikerar att det nuvarande beteendet är som det är designat, men att det möjligen är en designbegränsning eller en saknad funktion. Vissa av de numeriska operatorerna i TypeScript beter sig inte exakt som människor skulle vilja, men de trasiga eller saknade användningsfallen är inte tillräckligt kritiska för att åtgärdas (åtminstone inte omedelbart). I det här fallet ser jag inga tecken på att folk bryr sig tillräckligt om att lägga till två string | number-operander med + för att det ska stödjas uttryckligen.
Såvitt jag kan bedöma finns det inga GitHub-ärenden, öppna eller stängda, som efterfrågar ett sådant stöd.
Observera att det i allmänhet är avsiktligt att viss giltig JavaScript-kod anses ogiltig av TypeScript. Se Vad betyder "all legal JavaScript is legal TypeScript"? för en kanonisk förklaring. Det innebär att vi måste säga något mer än "detta fungerar i JavaScript" för att motivera att det tillåts i TypeScript. Vi måste säga "denna kod är sannolikt avsiktlig och inte en bugg".
Det är inte uppenbart att lägga ihop två string | number är något som folk vill göra särskilt ofta; operationerna strängkonkatenering och numerisk addition är väldigt olika operationer som råkar vara tillgängliga via +. Du kan tänka på + som en överlagrad funktion, och TypeScript tillåter inte att du anropar överlagrade funktioner med unioner av parametertyper från varje anropssignatur; se microsoft/TypeScript#14107 för en funktionsförfrågan om att tillåta sådana anrop.
Men det är möjligt att någon skulle kunna öppna ett ärende och begära stöd för att lägga till två string | number med korrekt motivering om att det är avsiktligt oftare än det är en bugg.
Jag tror dock att en sådan fråga inte skulle göra mycket nytta. Om vi tittar på microsoft/TypeScript#2031 ser vi en relaterad fråga om varför du inte kan lägga till två värden av typen Number (wrapper-objektet, inte number-primitivet). Vid den tidpunkt då detta lades, sade TypeScript-språkspecifikationen att den binära +-operatorn är tillåten i ett uttryck a + b om
- typerna av både
aochbkan tilldelasnumber, i vilket fall resultatet är ettnumber; eller - typerna av antingen
aellerbkan tilldelas tillstring, i vilket fall resultatet är enstring; eller - typerna av antingen
aellerbärany, i vilket fall resultatet är ettany.
Ingen av dessa tillåter Number (eller faktiskt string | number). Det är troligt att dessa tre möjligheter står för den stora majoriteten av användningen av binär +, och även om det kan vara trevligt att stödja andra fall är de inte brådskande. Stöd för saker som Number föreslogs i microsoft/TypeScript#2361, där det har legat still i åratal utan att implementeras.
Så det är tydligt att det inte är kritiskt för någon på TS-teamet.
Om du vill kringgå detta själv, kan du alltid göra det, som
const add = (a: string | number, b: string | number) =>
((a as any) + b) as (string | number);
vilket alltid skulle producera string | number, eller följande distributiva konditionella generiska sak:
const add = <T extends string | number, U extends string | number>(t: T, u: U) =>
((t as any) + u) as (T extends number ? U extends number ? number : string : string);
const five = add(2, 3); // number
const ab = add("a", "b"); // string
const aThree = add("a", 3); // string
const twoB = add(2, "b"); // string
const alsoTwoB = add(Math.random() < 0.5 ? "2" : 2, "b"); // string
const roulette = add(Math.random() < 0.5 ? "2" : 2, 3); // string | number
vilket producerar en smal outputtyp som beror på typen av varje input.
Lekplatslänk till kod
Svar #2
Baserat på Jcalz svar.
För att förstå detta behöver vi fokusera vår uppmärksamhet på +-operatorn.
Som en operator har det inte en tydlig signatur, men Typescript-specifikationen anger vilka typer som kan förekomma på båda sidor.
Låt oss fokusera på numeriska och strängparametertyperna. Vi kan tänka oss att plus-operatorn har följande funktionsöverlagringar:
function plusOperator(left: number, right: number): number
function plusOperator(left: number, right: string): string
function plusOperator(left: string, right: number): string
function plusOperator(left: string, right: string): string
För närvarande tillåter inte Typescript oss att skicka string|number till ovanstående pluseOperator-funktion, även om vi vet att funktionen kan hantera det. Detta är något kontroversiellt och en begäran om att ändra detta beteende är fortfarande öppen (se issue 14107).
