generics - TypeScript-tupler villkorlig jämförelse utvärderas alltid till falskt
Detta är väldigt enkelt, här är länken till lekplatsen
Som jag har lagt märke till, när du tar bort tuplet-typen från villkoret i If<> går allt som förväntat. Men jag vill inte göra det (t2-påståendet kraschar), dessutom skulle jag vilja förstå varför detta händer. Är detta bara en bugg? Varför expanderar typen And<> alltid till true?
type If<
TCond extends boolean,
TIfTrue,
TElse
> = [TCond] extends [true] ? TIfTrue : TElse; // if you remove tuples, it works
type Not<T extends boolean> = If<(T), false, true>;
type IsNever<TSuspect> = TSuspect extends never ? true : false;
type AssertFalse<TSuspect extends false> = TSuspect;
type AssertTrue <TSuspect extends true> = TSuspect;
// V~~ always true
type And<T extends boolean[]> = Not<Not<IsNever<Extract<T[number], false>>>>;
type AndExpected<T extends boolean[]> = IsNever<Extract<T[number], false>>;
type t0 = AssertFalse<Not<true>>;
type t1 = AssertTrue<Not<false>>;
type t2 = AssertTrue<Not<boolean>>;
type t3 = AssertFalse<And<[false]>>; // ????
type t4 = AssertFalse<AndExpected<[false]>>;
Svar #1
Först ska jag försöka förklara varför If med tupler skiljer sig från If utan tupler:
type If<
TCond extends boolean,
TIfTrue,
TElse
> = [TCond] extends [true] ? TIfTrue : TElse; // if you remove tuples, it works
type If2<
TCond extends boolean,
TIfTrue,
TElse
> = TCond extends true ? TIfTrue : TElse;
type Not<T extends boolean> = If<(T), false, true>;
type Not2<T extends boolean> = If2<(T), false, true>;
// with tuples, Not<boolean> is true because that's how you set it up
type A1 = Not<boolean>; // true
type A2 = Not<true>; // false
type A3 = Not<false>; // true
// without typles, TCond extends true is distributive over union types,
// and boolean is really just a union of true | false,
// so Not2<boolean> is boolean
type B1 = Not2<boolean>; // boolean
type B2 = Not2<true>; // false
type B3 = Not2<false>; // true
okej, nu till frågan. Här är det märkliga jag (och Titian Cernicova-Dragomir) upptäckte:
type SameAsT<T extends boolean> = Not<Not<T>>;
type X1 = SameAsT<false>; // true !
type X2 = SameAsT<true>; // true again
// but why?
Om du expanderar alla literala typer fungerar det som förväntat:
type ReallyNotFalse = [false] extends [true] ? false : true; // true
type ReallyNotNotFalse = [([false] extends [true] ? false : true)] extends [true] ? false : true; // false
Ser ut som en bugg i kompilatorn.
För övrigt fungerar Not baserat på If utan tupler som förväntat
type SameAsT2<T extends boolean> = Not2<Not2<T>>;
type Y1 = SameAsT2<false>; // false
type Y2 = SameAsT2<true>; // true
Så, det är möjligt att använda något annat sätt för att undertrycka distributiv unionstyp i villkor och få den aktuella koden att fungera. Ett sätt är att lägga till ett överflödigt villkor som alltid utvärderas till sant och inte har TCond som den typ som kontrolleras:
type If<
TCond extends boolean,
TIfTrue,
TElse
> = {} extends TCond ? TCond extends true ? TIfTrue : TElse : never;
type Not<T extends boolean> = If<(T), false, true>;
type IsNever<TSuspect> = TSuspect extends never ? true : false;
type AssertFalse<TSuspect extends false> = TSuspect;
type AssertTrue <TSuspect extends true> = TSuspect;
// V~~ always true
type And<T extends boolean[]> = Not<Not<IsNever<Extract<T[number], false>>>>;
type AndExpected<T extends boolean[]> = IsNever<Extract<T[number], false>>;
type t0 = AssertFalse<Not<true>>;
type t1 = AssertTrue<Not<false>>;
type t2 = AssertTrue<Not<boolean>>;
type t3 = AssertFalse<And<[false]>>;
type t4 = AssertFalse<AndExpected<[false]>>;
