打字稿检查对象中的属性是否以类型安全的方式

cdb*_*a89 17 javascript types undefined typescript

编码

const obj = {};
if ('a' in obj) console.log(42);
Run Code Online (Sandbox Code Playgroud)

不是打字稿(没有错误)。我明白为什么会这样。此外,在 TS 2.8.1 中,“in”用作类型保护。

但是,有没有办法检查属性是否存在,但如果属性未在obj的接口中定义,则会出错?

interface Obj{
   a: any;
}
Run Code Online (Sandbox Code Playgroud)

我不是在谈论检查未定义...

Kok*_*oko 14

您不会收到错误消息,因为您使用字符串来检查该属性是否存在。

你会以这种方式得到错误:

interface Obj{
   a: any;
}

const obj: Obj = { a: "test" };

if (obj.b)          // this is not allowed
if ("b" in obj)     // no error because you use string
Run Code Online (Sandbox Code Playgroud)

如果您希望类型检查适用于字符串属性,您可以使用此示例添加索引签名

  • 但是“if (obj.b)”也不允许未定义。属性不存在和未定义之间存在差异。我只想检查该属性是否以类型安全的方式存在。 (3认同)
  • 仍然不是类型安全检查,但您的答案是最好的,因为只有一个。我总是可以编写一个函数来进行类型安全的“in”检查(使用 typescript keyof 语法) (3认同)

Sam*_*mmi 9

您可以围绕 hasOwnProperty 实现自己的包装函数,以进行类型缩小。

function hasOwnProperty<T, K extends PropertyKey>(
    obj: T,
    prop: K
): obj is T & Record<K, unknown> {
    return Object.prototype.hasOwnProperty.call(obj, prop);
}
Run Code Online (Sandbox Code Playgroud)

我在这里找到了这个解决方案: TypeScript typearrowingnotworkingwhenlooping

用法:

const obj = {
    a: "what",
    b: "ever"
} as { a: string }

obj.b // Type error: Property 'b' does not exist on type '{ a: string; }'

if (hasOwnProperty(obj, "b")) {
    // obj is no longer { a: string } but is now
    // of type { a: string } & Record<"b", unknown>
    console.log(obj.b)
}
Run Code Online (Sandbox Code Playgroud)

此方法的限制是您只能返回包含您指定的添加的单个键的记录。这可能适合某些需求,但如果您需要更通用的解决方案,那么我建议像 Zod 这样的库,它可以验证复杂的对象并为您提供完整的类型: https: //github.com/colinhacks/zod

  • 我感到困惑的是,内置属性检查尚未缩小这样的类型。我将其添加到我的默认“包含在我的所有 TypeScript 项目中”代码中。 (4认同)

qua*_*oke 6

以下handle函数检查假设的服务器响应类型安全方式:

/**
 * A type guard. Checks if given object x has the key.
 */
const has = <K extends string>(
  key: K,
  x: object,
): x is { [key in K]: unknown } => (
  key in x
);

function handle(response: unknown) {
  if (
    typeof response !== 'object'
    || response == null
    || !has('items', response)
    || !has('meta', response)
  ) {
    // TODO: Paste a proper error handling here.
    throw new Error('Invalid response!');
  }

  console.log(response.items);
  console.log(response.meta);
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接。函数has可能应该保存在单独的实用程序模块中。

  • 呃...我已经发布了当您无法控制函数“handle”的参数类型时的解决方案。例如,这可能是 API 端点处理程序——每个人都可能意外地向那里发送无效数据。这就是为什么“handle”接受“unknown”类型的参数(这意味着“一切,我不知道是什么”)。这里的类型安全意味着您无法(在编译时)在不检查其类型的情况下使用“response”。例如,[此处](https://cutt.ly/qdS5nvj) 第 24 行中的 `response.somethingElse` 在没有进行类型检查的情况下使用,TypeScript 对此有所抱怨。 (2认同)