【问题标题】:Keyof not working in template literals due to Symbols由于符号,Keyof 在模板文字中不起作用
【发布时间】:2022-08-07 04:38:38
【问题描述】:

我希望这两种类型中的任何一种都能正常工作,但它们都会引发错误。

export type Object1<T extends { [k: string]: any }> = `${keyof T}`;
export type Object2<T extends Record<string, any>> = `${keyof T}`;

他们都给出了这个错误

TS2322: Type \'key T\' is not assignable to type \'string | number | bigint | boolean | null | undefined\'. 
 Type \'string | number | symbol\' is not assignable to type \'string | number | bigint | boolean | null | undefined\'.

我不明白为什么symbol | number 会出现,尽管指定键是字符串。

标签: typescript


【解决方案1】:

问题是约束不会将键限制为字符串。他们要求T 中的所有字符串键都具有any 类型的值,但T 也可以拥有其他键。

例如,如果您省略模板文字类型以消除错误,则可以将Object1 应用于具有字符串、数字和符号键的对象类型:

type Object1<T extends {[k: string]: any}> = keyof T

type Test1 = Object1<{s: 'str', 1: 'num', [Symbol.iterator]: 'sym'}>
// type Test1 = typeof Symbol.iterator | "s" | 1

Object2 也是如此:

type Object2<T extends Record<string, any>> = keyof T

type Test2 = Object2<{s: 'str', 1: 'num', [Symbol.iterator]: 'sym'}>
// type Test1 = typeof Symbol.iterator | "s" | 1

TypeScript playground

【讨论】:

  • "the constraints don't restrict the keys to strings" 我认为这是让我感到困惑的部分。似乎设置 [k: string] 会将所有键限制为字符串,如果不是的话,甚至允许暗示约束的语法有什么意义?
  • @JDFrancis 记录约束允许您为某些键指定值类型,这很有用。但是,由于T延伸记录它可能还有其他键。这对于任何分配都是一样的:如果您有一个带有字符串键 const x = {x: 'str'} 的对象,您仍然可以将其分配给数字键 const r: Record&lt;number, boolean&gt; = x (tsplay.dev/wQVqvN) 的记录。
  • 嗯,有道理。有什么方法可以实现我正在寻找的行为吗?作为一种以keyof 只返回字符串的方式强类型化对象键的方式?
  • @JDFrancis 我不这么认为。您可以使用 type StrObj = {[k: string]: any, [k: number | symbol]: never} 之类的东西来限制创建的对象,但它不会限制键。虽然可能有一些我不知道的技巧,所以你可以尝试在另一个问题中专门询问这个问题。
【解决方案2】:

@Oblosys 的答案很好地解释了为什么 keyof 仍然会产生所有可能的键类型,即使您指定了约束。如果我正确理解您的问题,您的目标是在字符串文字中获取类型的所有键(也许用于进一步的字符串操作?)

也许这可以帮助你:

export type KeyLiterals<T extends {[k: string] : any}> = 
  `${keyof T extends string | number ? keyof T : never}`;

type Test = KeyLiterals<{a: string, b: string}> // 'a' | 'b'

我们可以检查keyof 是否属于string 或number 类型,以确保没有symbols 留在联合中。


现在您可以进行任何进一步的字符串操作,例如添加前缀。

export type KeyLiterals2<T extends {[k: string] : any}> = 
  `prefix_${keyof T extends string | number ? keyof T : never}`;

type Test2 = KeyLiterals2<{a: string, b: string}> // 'prefix_a' | 'prefix_b'

【讨论】:

    【解决方案3】:

    这是正常行为,请阅读此issue

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-08
      • 2016-02-28
      • 1970-01-01
      相关资源
      最近更新 更多