TypeScript - Problem cu tipurile union și tipurile condiționale
Am următoarele declarații de tip:
class MyGeneric<T> { }
type ReplaceType<T> = T extends Function ? T : MyGeneric<T> | T;
ReplaceType<T> ar trebui să se rezolve fie la MyGeneric<T> | T, fie la T, în funcție de faptul dacă T este o funcție sau nu:
// Input type: string
// Expected type: string | MyGeneric<string>
// Actual type: string | MyGeneric<string>
type Test1 = ReplaceType<string>;
// Input type: () => void
// Expected type: () => void
// Actual type: () => void
type Test2 = ReplaceType<() => void>;
Din păcate, acest lucru nu funcționează corect cu boolean și tipurile uniune:
// Input type: boolean
// Expected type: boolean | MyGeneric<boolean>
// Actual type: boolean | MyGeneric<true> | MyGeneric<false>
type Test3 = ReplaceType<boolean>;
// Input type: "foo" | "bar"
// Expected type: "foo" | "bar" | MyGeneric<"foo" | "bar">
// Actual type: "foo" | "bar" | MyGeneric<"foo"> | MyGeneric<"bar">
type Test4 = ReplaceType<"foo" | "bar">;
Link către terenul de joacă
Răspuns #1
Motivul pentru care boolean și uniunile au comportamente similare este că compilatorul vede boolean ca o uniune a tipurilor literale true și false, deci type boolean=true | false (deși această definiție nu există explicit)
Motivul comportamentului este că, prin proiectare, tipul condițional se distribuie peste o uniune. Acesta este comportamentul proiectat și permite implementarea tuturor felurilor de lucruri puternice. Puteți citi mai multe despre acest subiect aici
Dacă nu vrei ca condiționalele să se distribuie peste uniune, poți folosi tipul într-un tuplu (acest lucru va preveni comportamentul)
class MyGeneric<T> { }
type ReplaceType<T> = [T] extends [Function] ? T : MyGeneric<T> | T;
// Input type: string
// Expected type: string | MyGeneric<string>
// Actual type: string | MyGeneric<string>
type Test1 = ReplaceType<string>;
// Input type: () => void
// Expected type: () => void
// Actual type: () => void
type Test2 = ReplaceType<() => void>;
// Input type: boolean
// Expected type: boolean | MyGeneric<boolean>
// Actual type: boolean | MyGeneric<boolean>
type Test3 = ReplaceType<boolean>;
// Input type: "foo" | "bar"
// Expected type: "foo" | "bar" | MyGeneric<"foo" | "bar">
// Actual type: "foo" | "bar" | MyGeneric<"foo" | "bar">
type Test4 = ReplaceType<"foo" | "bar">;
