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

TypeScript如何检查对象是否包含类型定义的全部属性

问题根源

TypeScript 的静态类型仅在编译阶段生效,编译完成后所有类型信息会被完全擦除,运行时不存在Command这类自定义类型的任何元数据,因此不可能直接把类型作为参数传给Object.keys()遍历属性做校验。
要实现无冗余的类型守卫,核心思路是维护一份和类型定义强绑定的运行时校验Schema,同时通过TS的类型检查保证Schema和类型定义始终同步,避免手动逐行写typeof判断的冗余,也不会出现类型更新后校验逻辑漏改的问题。


实现方案

首先写一个通用的结构校验工具函数,复用类型判断逻辑,避免每个类型都重复写判断链:

// 映射typeof操作符的返回值和对应TS类型
type TypeofMap = {
  string: string;
  number: number;
  boolean: boolean;
  function: Function;
  object: object | null;
  undefined: undefined;
  symbol: symbol;
  bigint: bigint;
};

/**
 * 通用对象结构校验
 * @param target 待校验值
 * @param schema 属性名 -> 期望typeof返回值 的映射表
 */
function checkShape<T extends Record<keyof T, keyof TypeofMap>>(
  target: unknown,
  schema: T
): target is { [K in keyof T]: TypeofMap[T[K]] } {
  // 先排除非对象、null的非法输入
  if (typeof target !== 'object' || target === null) return false;
  // 遍历schema做批量判断,不用逐行写&&链式判断
  return Object.entries(schema).every(([key, expectType]) => {
    return Object.prototype.hasOwnProperty.call(target, key) 
      && typeof (target as Record<string, unknown>)[key] === expectType;
  });
}

针对Command类型,只需要定义对应的校验Schema,同时加一层编译时校验,保证Schema覆盖Command的所有必填属性,只要后续Command类型新增/删除属性,编译阶段就会直接报错,强制同步校验逻辑:

// Command类型定义
type Command<DataPerGuildType> = {
  name: string;
  execute(message: Message, guildData: DataPerGuildType, args: string[]): Promise<void>;
};

// 运行时校验Schema,新增属性只要在这里加一行即可
const commandSchema = {
  name: 'string',
  execute: 'function',
} as const;

// 编译时校验:保证Schema和Command类型的必填属性完全对齐
type ValidateSchema<S> = S extends Record<keyof Command<unknown>, keyof TypeofMap> ? true : false;
// 如果Schema漏了Command的属性,这行编译直接报错
const _schemaValid: ValidateSchema<typeof commandSchema> = true;

// 最终的类型守卫
const isCommand = <GuildDataType>(
  toCheck: unknown,
): toCheck is Command<GuildDataType> => {
  if (!checkShape(toCheck, commandSchema)) return false;
  // 如果需要更严格校验execute的返回值是Promise,可以在这里加额外判断
  // 注意:运行时无法校验execute的参数类型、泛型GuildDataType的具体结构,这是TS类型擦除的特性决定的
  return true;
};

注意事项
  • 不要把待校验参数的类型标注为object,应该用unknown,前者无法安全做索引访问,也无法提前过滤null这类typeof返回'object'的非法值
  • 受限于TS类型擦除的设计,运行时无法校验泛型参数GuildDataType的结构,也无法校验execute方法的参数、返回值类型是否完全匹配,如果需要强校验,只能在实际调用execute的逻辑里补充运行时检查
  • 校验属性存在性时用Object.prototype.hasOwnProperty.call(target, key)而不是直接调用target.hasOwnProperty,避免对象原型被篡改导致的判断错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 06:15:44