如何用TypeScript强制泛型的所有属性都出现在嵌套数组中?
实现表单布局的全属性类型检查
要让TypeScript检查表单布局是否包含User的所有属性,核心思路是追踪布局中实际包含的字段key,然后约束这些key的联合必须等于User的所有属性。以下是具体实现方案:
1. 保留字段的具体key类型
首先修改field和layout函数的泛型定义,让它们保留每个字段的具体key信息,而非使用宽泛的keyof T:
type Field<T, K extends keyof T> = { fieldName: K; value: T[K]; }; // 捕获当前布局组包含的具体字段key function layout<T, K extends keyof T>(fields: Array<Field<T, K>>): Array<Field<T, K>> { return fields; } function field<T, K extends keyof T>(fieldName: K, value: T[K]): Field<T, K> { return { fieldName, value, }; } interface User { firstName: string; lastName: string; age: number; gender: string; }
2. 提取布局中的所有字段key
定义辅助类型,递归提取嵌套布局数组中所有fieldName的联合类型:
// 提取嵌套布局中所有fieldName的联合类型 type ExtractAllFieldNames<Layout> = Layout extends Array<infer Group> ? Group extends Array<infer F> ? F extends Field<any, infer K> ? K : never : never : never;
3. 定义全属性约束的布局类型
创建FieldsGroupLayout类型,通过两层条件检查确保布局包含所有属性:
- 检查布局中的字段都是
T的合法属性 - 检查
T的所有属性都在布局中存在
// 约束布局必须包含T的所有属性 type FieldsGroupLayout<T, Layout extends Array<Array<Field<T, any>>>> = ExtractAllFieldNames<Layout> extends keyof T ? keyof T extends ExtractAllFieldNames<Layout> ? Layout : never // 缺失属性时返回never,触发类型错误 : never; // 存在非法属性时返回never
4. 应用类型检查
使用TypeScript 4.9+支持的satisfies关键字(或类型断言)进行检查:
合法示例(无错误)
const validForm = [ layout([ field<User>('firstName', 'John'), field<User>('lastName', 'Doe'), ]), layout([ field<User>('age', 12), field<User>('gender', 'Male'), ]), ] satisfies FieldsGroupLayout<User, typeof validForm>;
非法示例(缺失属性,触发类型错误)
// 缺失gender属性,TypeScript会报错 const invalidForm = [ layout([ field<User>('firstName', 'John'), field<User>('lastName', 'Doe'), ]), layout([ field<User>('age', 12), ]), ] satisfies FieldsGroupLayout<User, typeof invalidForm>;
如果使用低于4.9的TypeScript版本,可改用类型断言:
const validForm = [/*...*/] as FieldsGroupLayout<User, typeof validForm>;
内容的提问来源于stack exchange,提问作者Miki
相关产品推荐
相关产品推荐

