如何在TypeScript中创建类型,强制对象拥有对应静态数组值的属性
解决方案
要实现你需要的类型约束和智能提示,可以通过类型映射、分布式联合类型以及自定义类型守卫来完成,具体步骤如下:
1. 定义Flag与属性的映射关系
首先把每个Flag对应的属性类型从原类型(A、B)中提取出来,建立清晰的映射:
type A = { flags: ['flagNameA']; propertyA1: string; propertyA2: string }; type B = { flags: ['flagNameB']; propertyB1: number }; // 提取每个Flag对应的属性(移除flags字段) type FlagPropsMap = { flagNameA: Omit<A, 'flags'>; flagNameB: Omit<B, 'flags'>; }; // 定义所有合法的Flag名称 type FlagName = keyof FlagPropsMap;
2. 生成SomeType类型
通过递归类型生成所有可能的Flag子集,并为每个子集生成对应的约束类型,确保flags包含的Flag必须对应声明其属性:
// 递归生成所有Flag子集的字符串表示(如"flagNameA"、"flagNameA,flagNameB") type AllSubsets<T extends string> = T extends infer U ? U | `${U},${AllSubsets<Exclude<T, U>>}` : never; // 将逗号分隔的子集字符串转换为数组类型 type StringToArray<S extends string> = S extends `${infer A},${infer B}` ? [A, ...StringToArray<B>] : [S]; // 生成所有合法的Flag子集数组类型 type FlagSubsetArrays = StringToArray<AllSubsets<FlagName>>; // 为每个Flag子集生成对应的类型:flags数组 + 对应属性的交叉类型 type CombinationType<C extends FlagName[]> = { flags: C } & { [K in C[number]]: FlagPropsMap[K] }; // 最终的SomeType是所有子集类型的联合 type SomeType = CombinationType<FlagSubsetArrays[number]>;
3. 验证类型约束
现在你可以测试示例代码,TypeScript会自动检查属性是否与flags匹配:
const var1: SomeType = { // ✅ 合法 flags: ['flagNameA'], propertyA1: 'value', propertyA2: 'value', }; const var2: SomeType = { // ✅ 合法 flags: ['flagNameB'], propertyB1: 1, }; const var3: SomeType = { // ✅ 合法 flags: ['flagNameA', 'flagNameB'], propertyA1: 'value', propertyA2: 'value', propertyB1: 1, }; const var4: SomeType = { // ❌ 报错:缺少propertyB1属性 flags: ['flagNameA', 'flagNameB'], propertyA1: 'value', propertyA2: 'value', };
4. 实现智能提示与类型安全
通过自定义类型守卫函数,在判断flags包含某个Flag时,自动缩小类型范围,确保属性访问安全:
// 自定义类型守卫:判断对象是否包含指定Flag,并缩小类型 function hasFlag<T extends SomeType, F extends FlagName>(obj: T, flag: F): obj is T & FlagPropsMap[F] { return obj.flags.includes(flag); } // 使用示例 if (hasFlag(var3, 'flagNameA')) { // 🎉 输入'flagName'时有智能提示 var3.propertyA1 = 'otherValue'; // ✅ 类型安全,已确认存在该属性 var3.propertyB2 = 2; // ❌ 报错:不存在该属性 }
补充说明
- 如果后续新增C、D等类型,只需在
FlagPropsMap中添加对应的键值对即可,无需修改其他类型定义。 - 递归生成子集的方式适用于Flag数量较少的场景(一般不超过10个),过多Flag会导致TypeScript性能下降,此时可以手动枚举常用的Flag组合替代递归生成。
内容的提问来源于stack exchange,提问作者jaime martin
相关产品推荐
相关产品推荐

