Lar*_*rry 5 typescript typescript-typings
我想要一种类型,它将产生另一种类型的键,其属性与给定类型匹配:
type KeyOfType<T, U extends T[keyof T]> = keyof T; // ?? type such that T[KeyOfType<T, U>] == U
Run Code Online (Sandbox Code Playgroud)
例如,考虑接口 I:
interface I {
a: string;
b: number|string;
c: string|number;
d: string;
}
Run Code Online (Sandbox Code Playgroud)
我想要以下等价物:
type T1 = KeyOfType<I, string>; // 'a'|'d'
type T2 = KeyOfType<I, string|number>; // 'b'|'c'
type T3 = KeyOfType<I, number>; // never
Run Code Online (Sandbox Code Playgroud)
我接近以下内容:
type KeyOfTypeTest<T, U> = NonNullable<{
[K in keyof T]: U extends T[K] ? T[K] extends U ? K : never : never;
}[keyof T]>;
Run Code Online (Sandbox Code Playgroud)
如果不涉及 OR 类型,则此方法可以正常工作:
type T4 = KeyOfTypeTest<I, string>; // 'a'|'d' as expected
Run Code Online (Sandbox Code Playgroud)
但联合类型失败:
type T5 = KeyOfTypeTest<I, string|number>; // expected 'b'|'c', but got 'a'|'d'
Run Code Online (Sandbox Code Playgroud)
知道我做错了什么吗?谢谢!
jca*_*alz 11
您的问题是检查U extends T[K] ? ... : ...意外地是分布式条件类型。由于U是一个裸泛型类型参数,因此编译器将其拆分为联合组成部分,评估每个组成部分的条件类型,并生成结果的联合。在许多情况下,这是理想的行为,但并不是您想要的。
为了防止这种情况发生,您必须通过对其应用某种类型函数来“装饰”裸类型参数。最简洁的方法是更改U extends T[K] ? ...为[U] extends [T[K]] ? ...:
type KeyOfTypeTest<T, U> = NonNullable<{
[K in keyof T]: [U] extends [T[K]] ? T[K] extends U ? K : never : never;
}[keyof T]>;
Run Code Online (Sandbox Code Playgroud)
并验证它是否符合您的要求:
type T4 = KeyOfTypeTest<I, string>; // 'a'|'d'
type T5 = KeyOfTypeTest<I, string | number>; // 'b'|'c'
Run Code Online (Sandbox Code Playgroud)
详细信息:将 的两边都包装extends在单元素元组运算符中的原因是因为数组和元组类型在 TypeScript 中是协变的,并且协变类型运算符保留了这种extends关系;如果你有一个协变运算符F<T>,那么X extends Y当且仅当F<X> extends F<Y>。如果您使用一些非协变类型运算符,您仍然会关闭分布式条件类型检查,但也会破坏您正在执行的检查。例如, in type G<X> = (x: T)=>void,G不是协变运算符,因此G<U> extends G<T[K]>不会按您希望的方式工作。
无论如何,我通常将这种“值与某种类型匹配的键”操作称为“值与某种类型匹配的键”操作KeysMatching<T, V>,您可以在其中找到以某种方式T“匹配”的类型的键V,如本答案所示。
重要的是要考虑您是要支持从T类型的变量读取属性并将其分配给 类型的变量V,还是从类型的值读取V并将其分配给 的属性T,或两者兼而有之。这三个选项导致三个不同的定义:
type KeysAssignableTo<T, V> =
{ [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];
type KeysAssignableFrom<T, V> =
{ [K in keyof T]-?: [V] extends [T[K]] ? K : never }[keyof T];
type KeysAssignableBothToAndFrom<T, V> =
{ [K in keyof T]-?: [T[K], V] extends [V, T[K]] ? K : never }[keyof T];
type X = KeysAssignableTo<I, string | 3> // "a" | "d"
// props "a" | "d" can be assigned to a variable of type string | 3
type Y = KeysAssignableFrom<I, string | 3> // "b" | "c"
// a variable of type string | 3 can be assigned to a props "b" | "c"
type Z = KeysAssignableBothToAndFrom<I, string | 3> // never
// you can't both read and write a value of type string | 3 to and from any props
Run Code Online (Sandbox Code Playgroud)
您正在执行的操作与以下操作相同KeysAssignableBothToAndFrom:
type T4b = KeysAssignableBothToAndFrom<I, string> // "a" | "d"
type T5b = KeysAssignableBothToAndFrom<I, string | number> // "b" | "c"
Run Code Online (Sandbox Code Playgroud)
如果您确实需要这种相互可分配性,那就太好了。否则,您可能需要考虑您的用例是否更适合其他定义之一。
| 归档时间: |
|
| 查看次数: |
1350 次 |
| 最近记录: |