如何创建生成与给定类型匹配的对象键的类型 [英] How to create a type that yields keys of an object that match a given type
问题描述
我想要一个类型,它将生成其属性与给定类型匹配的另一个类型的键:
type KeyOfType<T, U extends T[keyof T]> = keyof T; // ?? type such that T[KeyOfType<T, U>] == U
例如,考虑接口i:
interface I {
a: string;
b: number|string;
c: string|number;
d: string;
}
我需要以下等同内容:
type T1 = KeyOfType<I, string>; // 'a'|'d'
type T2 = KeyOfType<I, string|number>; // 'b'|'c'
type T3 = KeyOfType<I, number>; // never
我接近了以下内容:
type KeyOfTypeTest<T, U> = NonNullable<{
[K in keyof T]: U extends T[K] ? T[K] extends U ? K : never : never;
}[keyof T]>;
如果不涉及OR类型,则此操作正常工作:
type T4 = KeyOfTypeTest<I, string>; // 'a'|'d' as expected
但联合类型失败:
type T5 = KeyOfTypeTest<I, string|number>; // expected 'b'|'c', but got 'a'|'d'
你知道我做错了什么吗?谢谢!
推荐答案
您的问题是U extends T[K] ? ... : ...
检查意外为distributive conditional type。由于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]>;
并验证它是否执行您想要的操作:
type T4 = KeyOfTypeTest<I, string>; // 'a'|'d'
type T5 = KeyOfTypeTest<I, string | number>; // 'b'|'c'
细节:它将extends
的两侧包装在一个元素的tuple运算符中的原因是array and tuple types are covariant in TypeScript和covariant type operators保持extends
关系;如果有协变运算符F<T>
,则X extends Y
当且仅当F<X> extends F<Y>
。如果您使用一些非协变类型运算符,您仍然会关闭分布式条件类型检查,但也会破坏您正在进行的检查。例如,在type G<X> = (x: T)=>void
中,G
不是协变运算符,因此G<U> extends G<T[K]>
不会以您希望的方式工作。
无论如何,我通常将这种键称为其值与某个类型匹配操作KeysMatching<T, V>
,您可以在其中找到T
与V
类型匹配的键,如this answer。
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
您正在执行的操作与KeysAssignableBothToAndFrom
相同:
type T4b = KeysAssignableBothToAndFrom<I, string> // "a" | "d"
type T5b = KeysAssignableBothToAndFrom<I, string | number> // "b" | "c"
如果你真的需要这种相互分配的能力,那太好了。否则,您可能需要考虑您的用例是否更适合其他定义之一。
这篇关于如何创建生成与给定类型匹配的对象键的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!