You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否编写以对象键为第二个参数的TypeScript类型谓词?

问题

假设API返回400错误时会抛出如下错误类型:

type ServerError = {
  status: string
};

type ServerErrorResponse = {
  response: {
    errors: ServerError[];
  };
};

为处理该响应,原本编写了两个结构相似的类型谓词函数:

function hasResponseKey(res: unknown): res is { response: unknown } {
  return typeof res === 'object' && res !== null && 'response' in res;
}

function hasErrorsKey(response: unknown): response is { errors: unknown } {
  return typeof response === 'object' && response !== null && 'errors' in response;
}

最终实现的校验函数如下:

function isResponseServerErrorResponse(
  res: unknown
): res is ServerErrorResponse {
  return (
    hasResponseKey(res) &&
    hasErrorsKey(res.response) &&
    Array.isArray(res.response.errors)
  );
}

但上述方案存在代码重复,希望通过传入键参数实现通用的类型谓词函数,比如:

function hasKey(res: unknown, key: string): res is { key: unknown } {
  return typeof res === 'object' && res !== null && key in res;
}

请问是否可以编写这样的、第二个参数为对象键的类型谓词?

回答

当然可以,但你写的版本存在类型推断问题——TypeScript无法将传入的字符串参数key和类型谓词里的键名关联起来,导致类型谓词中的{ key: unknown }里的key只是一个字面量字符串,而非你实际传入的键名。

要实现真正通用的带键参数的类型谓词,需要用泛型来约束键的类型,让TypeScript能正确推断出对象的结构:

function hasKey<K extends PropertyKey>(res: unknown, key: K): res is { [P in K]: unknown } {
  return typeof res === 'object' && res !== null && key in res;
}

这里的K extends PropertyKey确保key是合法的对象键类型(字符串、数字或符号),而{ [P in K]: unknown }则表示返回的类型是包含指定键K、值为unknown的对象。

用这个通用函数重构原来的校验函数,代码会更简洁:

function isResponseServerErrorResponse(res: unknown): res is ServerErrorResponse {
  return (
    hasKey(res, 'response') &&
    hasKey(res.response, 'errors') &&
    Array.isArray(res.response.errors)
  );
}

这样既消除了代码重复,又能让TypeScript正确进行类型推断——当hasKey(res, 'response')返回true时,TypeScript会自动将res的类型推断为包含response键的对象,后续访问res.response也不会有类型错误。

如果你想进一步约束值的类型,还可以扩展这个泛型函数,比如允许传入值的类型校验:

function hasKeyWithType<K extends PropertyKey, T>(
  res: unknown,
  key: K,
  typeCheck: (val: unknown) => val is T
): res is { [P in K]: T } {
  return typeof res === 'object' && res !== null && key in res && typeCheck((res as { [P in K]: unknown })[key]);
}

比如用它来直接校验errors是数组:

function isResponseServerErrorResponse(res: unknown): res is ServerErrorResponse {
  return (
    hasKey(res, 'response') &&
    hasKeyWithType(res.response, 'errors', Array.isArray)
  );
}

内容的提问来源于stack exchange,提问作者canecse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 02:10:39