typescript - Varför att använda typen never som arrayindex ger en union av typerna för arrayvärden
type T<A> = A[never];
const tuple = ['1', '2'] as const
const array = ['1', '2']
type UninType = T<typeof tuple> // "1" | "2"
type StringType = T<typeof array> // string
Varför görs det på det här sättet, vad är poängen med detta beteende
Svar #1
Kort svar:
Detta beteende är bara en oavsiktlig följd av hur indexerade åtkomsttyper implementerades. Det nämndes vid något tillfälle att T[never] borde vara never oavsett vad T är; detta åtgärdades delvis; för T utan indexsignaturer.
Fixen för indexsignaturer sköts upp eftersom det vid den tiden skulle ha brutit andra saker, och att ha T[never] lösa till indexsignaturtypen råkade förhindra att ett annat problem dök upp. Numera skulle det förmodligen vara okej att genomföra den fullständiga fixen, men jag antar att ingen någonsin tog tag i det, troligen eftersom det inte är ett riktigt kritiskt problem. Du kanske vill öppna ett nytt ärende och be om att detta åtgärdas, om du bryr dig mycket om det.
Långt svar:
Om du tittar på microsoft/TypeScript#11929, pull-begäran som implementerade indexerade åtkomsttyper, beskriver den den (dåvarande) processen för att bestämma typen T[K]:
- Om
Kär en typ som kan tilldelasnumberochTinnehåller en numerisk indexsignatur, ärT[K]typen av den numeriska indexsignaturen.- Om
Kär en typ som kan tilldelasstringochTinnehåller en strängindexsignatur, ärT[K]typen av den strängindexsignaturen.
I ditt fall, eftersom array- och tuple-typer har en numerisk indexsignatur, och eftersom typen never kan tilldelas till varje typ, kommer indexering av en array- eller tuple-typ med never ge typen av den numeriska indexsignaturen.
Det är i alla fall konsekvent med dina resultat.
Nu vid någon tidpunkt märkte folk att indexering i en typ som {a: string} med never resulterade i any och gav fel. Detta var inte önskvärt; det verkar som att man borde kunna indexera med never och resultatet borde också vara never, så här:
type SomeObj = {a: string}[never]
// type SomeObj = never
Så detta registrerades som ett ärende i microsoft/TypeScript#22042 och det implementerades i microsoft/TypeScript/pull/22787. (vilket är anledningen till att ovanstående SomeObj är never och inte any).
I kommentarerna om den åtgärden noterade någon att:
denna ändring påverkar endast typer utan indexsignatur. Så till exempel:
{[x: string]: boolean}[never]ärboolean(oförändrat beteende){foo: boolean}[never]ärnever(nytt beteende, tidigare var det ett fel)Med den förbehållningen är detta en icke-brytande förändring eftersom det endast påverkar fall som för närvarande är fel.
En av medlemmarna i TS-teamet svarar sedan att
Efter att ha diskuterat det personligen med @ahejlsberg, gillade vi inte att
({ [n: number]: boolean})[never]är inkonsekvent: det är inteneversom allt annat...
men de kunde inte åtgärda det. Vid den tiden var keyof ({[n: number]: boolean}) never istället för number. Jag tror att vi alla kan hålla med om att om XXX är {[n: number]: boolean}, då vill vi att XXX[keyof XXX] ska vara boolean. Men eftersom keyof XXX var never, tvingade detta dem att också ha XXX[never] som boolean och inte never. De kunde inte fixa det utan att bryta något som av en slump fungerade.
Sedan dess infördes en ändring i TypeScript 2.9 som tillåter number och symbol-typer att komma ut ur keyof, implementerad i microsoft/TypeScript#23592. Så nu har vi det rätta beteendet:
type KeyofNumericIdxSig = keyof { [x: number]: boolean }
// type KeyofNumericIdxSig = number
Men vid tiden då microsoft/TypeScript#22787 implementerades, hade detta inte gjorts. Och språkarkitekten @ahejlsberg själv sade:
[Det här PR] för oss närmare dit vi så småningom vill komma, dvs. att
T[never]alltid ärneveroavsettT. På längre sikt, om och när vi fixarkeyof { [x: number]: T }för att produceranumeric-string(eller vad vi än kallar det) istället förnever, kan vi då även ändra{ [x: number]: T }[never]till att varanever.
Men det ser ut som att de aldrig kom tillbaka och slutförde detta.
Så där är du. Det är möjligt att om någon skulle öppna en funktionsbegäran som länkar till både microsoft/TypeScript#22787 och microsoft/TypeScript#23592 och pekar på just den här kommentaren, skulle de säga "oj, ja, det gjorde vi aldrig" och implementera det. Eller de kanske säger "oj ja, vi försökte det och det orsakade problem någon annanstans." Eller till och med "vi vill inte oroa oss för mycket över detta om inte många fler klagar på det." Jag kan egentligen inte förutsäga vilket.
