如何实现TypeScript泛型数组类型谓词以校验对象属性类型?
解决方案:泛型类型谓词实现对象与类属性的数组类型校验
一、修复对象实例的数组属性校验函数
你之前的写法问题在于,TypeScript对T[P] is Array<any>这种属性级别的类型谓词,在处理带索引签名的对象时无法正确完成类型收窄。调整为断言整个对象的指定属性为数组类型即可解决:
function isArrayProperty<T, P extends keyof T>( obj: T, prop: P, ): obj is T & { [K in P]: Array<any> } { return Array.isArray(obj[prop]); }
用你的测试代码验证:
const a: { [key in string]: string | string[]} = { arrayProp: ["this", "is", "an", "array"], otherProp: "this is a string" }; Object.keys(a).forEach((prop: keyof typeof a) => { if (isArrayProperty(a, prop)) { // 此处a[prop]已被正确识别为数组类型 a[prop].push("another value"); } });
二、实现类构造函数的数组属性校验
由于TypeScript类型信息在编译后会被擦除,必须依赖你提到的运行时Schema来判断类属性类型。假设你的Schema是一个映射类构造函数到属性类型的结构,实现如下:
1. 定义基础类型与运行时Schema示例
type ClassConstr<T> = new (...props: any[]) => T; // 示例运行时Schema:根据你的实际结构调整 const classSchema = new Map<object, Record<string, "array" | "string" | "date">>();
2. 实现重载的校验函数
通过函数重载同时支持对象实例和类构造函数的校验:
// 重载1:校验对象实例的属性 function isArrayProperty<T, P extends keyof T>( obj: T, prop: P, ): obj is T & { [K in P]: Array<any> }; // 重载2:校验类构造函数对应的实例属性 function isArrayProperty<T, P extends keyof T>( obj: ClassConstr<T>, prop: P, ): T[P] is Array<any>; // 统一实现逻辑 function isArrayProperty<T, P extends keyof T>( obj: T | ClassConstr<T>, prop: P, ): boolean { if (typeof obj === "function") { // 从运行时Schema获取属性类型判断 const schema = classSchema.get(obj); return schema?.[prop as string] === "array"; } else { return Array.isArray(obj[prop]); } }
3. 使用示例
class A { someProp: any; } // 给类A注册运行时Schema classSchema.set(A, { someProp: "array" }); const a = new A(); a.someProp = ["some", "array"]; if (isArrayProperty(A, "someProp")) { // 此处a.someProp被正确识别为数组类型 a.someProp.push("new value"); }
关键说明
- 对象实例版本通过断言整个对象的属性类型,让TypeScript能正确完成索引签名类型的收窄。
- 类构造函数版本完全依赖运行时Schema,因为TypeScript不会保留类属性的类型信息到运行时,无法通过构造函数或原型直接判断属性类型。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

