TypeScript - Was ist der Unterschied zwischen den beiden Pick<T,K>-Util-Typ-Implementierungen
Ich probiere das aus: https://github.com/type-challenges/type-challenges/blob/master/questions/4-easy-pick/README.md
Ich kann den Unterschied zwischen ihnen nicht verstehen:
type MyPick<T, K> = T extends {} ? K extends keyof T ? {[P in K]: T[P]} : never : never
type MyPick<T extends {}, K extends keyof T> = {[P in K]: T[P]};
Du kannst es hier spielen: Linkbeschreibung hier eingeben
Testfall:
type A = {name: string; value: number;}
type B = MyPick1<A, 'name' | 'value'> // got {name: string} | {value: number}
type C = MyPick2<A, 'name' | 'value'> // got {name: string; value: number;}
Ich bin über die Ergebnisse verwirrt, und warum?
Antwort #1
Erstes MyPick:
type MyPick<T, K> = T extends {} ? K extends keyof T ? {[P in K]: T[P]} : never : never
Kann zwei beliebige generische Typen akzeptieren. Ich meine, man kann es mit MyPick<1,2>/never oder MyPick<[], false>/never aufrufen, was natürlich keinen Sinn ergibt, aber trotzdem erlaubt ist, weil die Einschränkungen im Inneren der Utility überprüft werden.
Zweite MyPick:
type MyPick<T extends {}, K extends keyof T> = {[P in K]: T[P]};
Dieses MyPick hat Einschränkungen für beide Argumente. Das erste Argument sollte {} erweitern, was praktisch jeder any-Typ ist, und das zweite Argument sollte ein Schlüssel des ersten Arguments sein.
Zum Beispiel:
type Test0 = MyPick1<2, 'toString'> // ok
type Test1 = MyPick1<2, 3> // expected error
Wie Sie vielleicht bemerkt haben, dürfen Sie immer noch das erste Argument als primitiven Typ übergeben, da jeder Wert in JS ein Objekt ist und daher eigene Schlüssel hat.
// "toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString"
type NumberKeys = keyof number;
Ich nehme an, Sie möchten eine Einschränkung anwenden, bei der der Benutzer nur erlaubt ist, ein Objekt als ersten Parameter zu übergeben, das bedeutet, dass Sie die Einschränkung Record<PropertyKey, any> verwenden sollten. Zum Beispiel:
type Test0 = MyPick1<{ age: number }, 'age'> // ok
type Test1 = MyPick1<2, 'toString'> // error
type Test2 = MyPick1<true, 3> // expected error
Warum gibt MyPick1 einen Union-Typ zurück?
Sieh das:
// Slightly reduced
type MyPick1<T, K> = K extends keyof T ? { [P in K]: T[P] } : never
type A = { name: string; value: number; }
type B = MyPick1<A, 'name' | 'value'> // got {name: string} | {value: number}
MyPick1 gibt wegen der Distributivität eine Vereinigung zurück. Bitte lesen Sie diesen Abschnitt der Dokumentation gründlich. Dies ist eine sehr wichtige Funktion.
Wenn sich der K-Unionstyp verteilt, bedeutet das, dass diese Zeile { [P in K]: T[P] } auf jede Union angewendet wird.
Siehe ein weiteres Beispiel, um es klarer zu machen:
type Distibutive<T> = T extends any ? T[] : never
type Result1 = Distibutive<'a' | 'b'> // "a"[] | "b"[]
// This is how you disable distributivity
type NonDistibutive<T> = [T] extends [any] ? T[] : never
type Result2 = NonDistibutive<'a' | 'b'> // ("a" | "b")[]
