打字稿:如何声明其值等于对象键的数组类型?
Typescript: How to declare a type of array whose values are equal to keys of object?
如何在这段代码中定义MyInterfaceKeys
?
interface MyInterface extends Record<string, any> {
Appearance?: "default" | "primary" | "link";
Size?: "small" | "medium" | "large";
Color?: string;
Block?: boolean;
}
type MyInterfaceKeys = (keyof MyInterface)[]
// ok => MyInterfaceKeys === ["Block", "Color"]
// ok => MyInterfaceKeys === ["Appearance"]
// Error => MyInterfaceKeys === ["UnknownKey"]
事实上,我想将对象属性转换为联合文字:
type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]
TypeScript 中的对象类型有一组 已知键 ,它们也对应于单个字符串或数字 literals (or symbols); and a set of index signature keys, which correspond to multiple possible keys at once (these used to just be string
or number
, but now you can use pattern template literals and symbols in index signatures。例如下面的类型:
type Foo = {
[k: string]: 0 | 1
x: 0,
y: 1
}
有两个已知密钥 "x"
和 "y"
以及一个索引签名密钥 string
。您的 MyInterface
类型有四个已知密钥,但它也有一个 string
索引签名密钥(因为它扩展了 Record<string, any>
,后者有一个 string
索引签名密钥)。
The keyof
operator produces the union 的所有键。对于 Foo
,即 "x" | "y" | string
,对于 MyInterface
,即 "Appearance" | "Size" | "Color" | "Block" | string
。
由于每个字符串文字(如 "x"
和 "y"
)都是 string
的子类型,因此字符串文字类型与 string
的联合只是 string
,编译器急切地将它缩减为这个。所以 keyof Foo
和 keyof MyInterface
就是 string
。在某种意义上,string
“吸收”了所有字符串文字键。
因此,如果有任何吸收它的索引签名密钥,您不能使用 keyof
来获取已知密钥。
那么,你能做什么?您可以做的最干净的事情是考虑重构您的代码,以便可以更轻松地捕获您想要的信息:
interface MyKnownInterface {
Appearance?: "default" | "primary" | "link";
Size?: "small" | "medium" | "large";
Color?: string;
Block?: boolean;
}
interface MyInterface extends Record<string, any>, MyKnownInterface { }
type KK = (keyof MyKnownInterface) & string
// type KK = "Appearance" | "Size" | "Color" | "Block"
type MyInterfaceKeys = (keyof MyKnownInterface)[]
// type MyInterfaceKeys = (keyof MyKnownInterface)[]
// type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]
这里的MyInterface
和之前的是等价的,但是keyof MyKnownInterface
是你想要的已知键的并集
你也可以使用类型操作来尝试梳理出已知的键,而不是仅仅使用 keyof
,尽管我认为我所知道的所有可能的方法都有一些奇怪的边缘情况(我即将提交一个与 symbol
键有关的错误)。但希望你不会 运行 陷入这种极端情况(MyInterface
示例不会)。
一种方法是使用key remapping in mapped types来抑制索引签名,它可以被识别为不需要值的任何键,即使它不是可选的(索引签名代表任意数量的键)正确的类型,包括零个这样的键):
type KnownKeys<T> = keyof {
[K in keyof T as {} extends { [P in K]: any } ? never : K]: never
}
如果我们在您的情况下这样做,您将得到:
type MyInterfaceKeys = (KnownKeys<MyInterface>)[]
// type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]
如何在这段代码中定义MyInterfaceKeys
?
interface MyInterface extends Record<string, any> {
Appearance?: "default" | "primary" | "link";
Size?: "small" | "medium" | "large";
Color?: string;
Block?: boolean;
}
type MyInterfaceKeys = (keyof MyInterface)[]
// ok => MyInterfaceKeys === ["Block", "Color"]
// ok => MyInterfaceKeys === ["Appearance"]
// Error => MyInterfaceKeys === ["UnknownKey"]
事实上,我想将对象属性转换为联合文字:
type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]
TypeScript 中的对象类型有一组 已知键 ,它们也对应于单个字符串或数字 literals (or symbols); and a set of index signature keys, which correspond to multiple possible keys at once (these used to just be string
or number
, but now you can use pattern template literals and symbols in index signatures。例如下面的类型:
type Foo = {
[k: string]: 0 | 1
x: 0,
y: 1
}
有两个已知密钥 "x"
和 "y"
以及一个索引签名密钥 string
。您的 MyInterface
类型有四个已知密钥,但它也有一个 string
索引签名密钥(因为它扩展了 Record<string, any>
,后者有一个 string
索引签名密钥)。
The keyof
operator produces the union 的所有键。对于 Foo
,即 "x" | "y" | string
,对于 MyInterface
,即 "Appearance" | "Size" | "Color" | "Block" | string
。
由于每个字符串文字(如 "x"
和 "y"
)都是 string
的子类型,因此字符串文字类型与 string
的联合只是 string
,编译器急切地将它缩减为这个。所以 keyof Foo
和 keyof MyInterface
就是 string
。在某种意义上,string
“吸收”了所有字符串文字键。
因此,如果有任何吸收它的索引签名密钥,您不能使用 keyof
来获取已知密钥。
那么,你能做什么?您可以做的最干净的事情是考虑重构您的代码,以便可以更轻松地捕获您想要的信息:
interface MyKnownInterface {
Appearance?: "default" | "primary" | "link";
Size?: "small" | "medium" | "large";
Color?: string;
Block?: boolean;
}
interface MyInterface extends Record<string, any>, MyKnownInterface { }
type KK = (keyof MyKnownInterface) & string
// type KK = "Appearance" | "Size" | "Color" | "Block"
type MyInterfaceKeys = (keyof MyKnownInterface)[]
// type MyInterfaceKeys = (keyof MyKnownInterface)[]
// type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]
这里的MyInterface
和之前的是等价的,但是keyof MyKnownInterface
是你想要的已知键的并集
你也可以使用类型操作来尝试梳理出已知的键,而不是仅仅使用 keyof
,尽管我认为我所知道的所有可能的方法都有一些奇怪的边缘情况(我即将提交一个与 symbol
键有关的错误)。但希望你不会 运行 陷入这种极端情况(MyInterface
示例不会)。
一种方法是使用key remapping in mapped types来抑制索引签名,它可以被识别为不需要值的任何键,即使它不是可选的(索引签名代表任意数量的键)正确的类型,包括零个这样的键):
type KnownKeys<T> = keyof {
[K in keyof T as {} extends { [P in K]: any } ? never : K]: never
}
如果我们在您的情况下这样做,您将得到:
type MyInterfaceKeys = (KnownKeys<MyInterface>)[]
// type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]