如何在TypeScript低版本中基于已有接口属性名创建带后缀的新接口?
在TypeScript 4.1以下版本实现关联属性接口的方案
由于TypeScript 4.1才引入键重映射和模板字面量类型这两个核心特性,在更低版本里确实没办法完全自动生成带Index后缀的属性名,但我们可以通过几种变通方案来减少重复代码、降低出错概率:
方案一:用枚举统一管理属性名
通过枚举把原属性和带后缀的属性名统一维护,避免手动拼写错误,同时让两个接口的属性名形成明确关联:
// 枚举定义所有需要的属性名,包含原属性和对应Index后缀的属性 enum PropKeys { PropOne = 'propOne', PropOneIndex = 'propOneIndex', PropTwo = 'propTwo', PropTwoIndex = 'propTwoIndex', PropThree = 'propThree', PropThreeIndex = 'propThreeIndex', } interface Int1 { [PropKeys.PropOne]: string; [PropKeys.PropTwo]?: number; [PropKeys.PropThree]?: Date; } interface Int2 { [PropKeys.PropOneIndex]: number; [PropKeys.PropTwoIndex]?: number; [PropKeys.PropThreeIndex]?: number; }
这个方案的优势是属性名统一管控,不会出现拼写失误;唯一的小缺点是当Int1新增属性时,需要同步在枚举和Int2里添加对应的Index项。
方案二:用条件类型关联属性可选性
如果更关注Int2属性的可选性和Int1保持自动同步,我们可以借助条件类型来实现这一点,属性名虽然还是要手动写,但可选性会自动跟随Int1变化:
interface Int1 { propOne: string; propTwo?: number; propThree?: Date; } // 通过条件类型自动推导Int2属性的可选性 type Int2 = { propOneIndex: number; } & (undefined extends Int1['propTwo'] ? { propTwoIndex?: number } : { propTwoIndex: number }) & (undefined extends Int1['propThree'] ? { propThreeIndex?: number } : { propThreeIndex: number });
比如如果后续把Int1里的propTwo改成必填项,Int2的propTwoIndex会自动变成必填,不需要手动修改。
方案三:辅助类型做事后验证(低版本兼容版)
如果想确保Int2完全符合规则,我们可以写一个辅助类型来做校验,一旦Int2的属性名、类型或可选性不符合要求,TypeScript就会报错:
interface Int1 { propOne: string; propTwo?: number; propThree?: Date; } // 针对低版本TS的辅助验证类型,手动检查每个属性的关联关系 type ValidateInt2<T> = { propOne: 'propOneIndex' extends keyof T ? T['propOneIndex'] extends number ? true : false : false; propTwo: 'propTwoIndex' extends keyof T ? T['propTwoIndex'] extends number ? undefined extends Int1['propTwo'] ? undefined extends T['propTwoIndex'] : true : false : false; propThree: 'propThreeIndex' extends keyof T ? T['propThreeIndex'] extends number ? undefined extends Int1['propThree'] ? undefined extends T['propThreeIndex'] : true : false : false; }; // 定义Int2后,通过辅助类型验证结构是否正确 interface Int2 { propOneIndex: number; propTwoIndex?: number; propThreeIndex?: number; } // 如果Int2不符合要求,这里会显示对应属性为false type CheckInt2Validity = ValidateInt2<Int2>;
这个方案相当于给Int2加了一层类型校验,能有效避免手动编写时的疏漏。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

