TypeScript 测试 Record 的值实例

edA*_*a-y 9 typescript

有没有办法测试一个值是否是 TypeScript 中类型的实例Record?我想要一个能够正确区分变量何时也可能是数组的情况。

function(a: Record<string,string>|string[]) {
  if( a instanceof Record ) {
     do_stuff( a.key )
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道这在 JavaScript 中是有问题的,因为我的两种类型都是对象,因此typeof(a) == "object"在这两种情况下都是对象。我希望可能有一种特殊的 TypeScript 方法来完成此任务。

我知道该Record类型在运行时并不真正存在,但该示例应该阐明我想要做什么。同样重要的是,测试正确地通知 TypeScript 缩小的类型,这样就a.key不会产生错误。

小智 3

根据文档, aRecord<K, V>是一个对象,其键为K,值为V。由于原型链, “true”Record<string, string>不存在,这导致对象继承例如方法.toString()。但考虑到完整的继承属性集会很麻烦(TS GitHub 上的相关讨论),因此最清楚的是考虑Record使用对象自身属性的类型:

// Allowed:
const foo: Record<string, string> = { bar: "bar" }

// Even though functions aren't a subtype of string:
type inheritedToString = typeof foo.toString
//   ~~~~~~~~~~~~~~~~~
//   inheritedToString = () => string
Run Code Online (Sandbox Code Playgroud)

这意味着您很可能通过将一种或多种迭代对象属性的方法与 TypeScript 的类型谓词相结合来取得足够的成果:

function isStringRecord(obj: unknown): obj is Record<string, string> {
  if (typeof obj !== "object")
    return false

  if (Array.isArray(obj))
    return false

  if (Object.getOwnPropertySymbols(obj).length > 0)
    return false
  
  return Object.getOwnPropertyNames(obj)
    .every(prop => typeof obj[prop] === "string")
}
Run Code Online (Sandbox Code Playgroud)

现在您将拥有:

const foo = { bar: "bar" } as unknown

if (isStringRecord(foo)) {
  foo // foo: Record<string, string>
}
Run Code Online (Sandbox Code Playgroud)