【问题标题】:Typescript: How to declare a type of array whose values are equal to keys of object?Typescript:如何声明一种值等于对象键的数组类型?
【发布时间】:2021-11-06 21:40:39
【问题描述】:

如何在这段代码中定义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"]

其实我是想把对象 props 转换成 union 字面量:

type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]

【问题讨论】:

标签: typescript typescript-types


【解决方案1】:

TypeScript 中的对象类型有一组已知键,它们对应于单个字符串或数字literals(或符号);和一组index signature 键,它们一次对应多个可能的键(这些过去只是stringnumber,但现在你也可以使用pattern template literals and symbols in index signatures)。例如下面的类型:

type Foo = {
    [k: string]: 0 | 1
    x: 0,
    y: 1
}

有两个已知密钥"x""y" 和一个索引签名密钥string。您的MyInterface 类型有四个已知密钥,但它也有一个string 索引签名密钥(因为它扩展了Record&lt;string, any&gt;,它有一个string 索引签名密钥)。


The keyof operator 产生所有键的union。对于Foo,即"x" | "y" | string,对于MyInterface,即"Appearance" | "Size" | "Color" | "Block" | string

由于每个字符串文字(如"x""y")都是string 的子类型,因此字符串文字类型与string 的并集就是string,编译器急切地将其简化为这个。所以keyof Fookeyof 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")[]

Playground link to code

【讨论】:

  • 太完美了:)))
猜你喜欢
  • 2016-01-13
  • 1970-01-01
  • 2021-10-24
  • 1970-01-01
  • 2022-11-25
  • 1970-01-01
  • 2022-08-22
  • 2022-01-26
  • 1970-01-01
相关资源
最近更新 更多