TypeScript映射类型:开发属性名对齐、支持类型自定义的SafeRetype泛型
需求说明
首先定义三个基础类型:
type A1 = { m1: boolean }; type A2 = { m2: number }; type B = { m1: string; m2: string };
需要实现的类型规则:
- 新类型的属性名必须和参考类型完全对齐
- 允许自定义属性的类型,不需要和参考类型的属性类型兼容
- 传入的自定义类型只能包含参考类型的全部或部分属性,不允许新增参考类型不存在的属性
预期示例
符合规则的场景
// 初始实现 type SafeRetype<T, Z extends T> = { [K in keyof T]?: Z[K]; }; // 传入Z为A1 | A2,符合规则 type C1 = SafeRetype<B, A1 | A2>;
当前初始实现存在问题:因为约束Z extends T要求Z的属性类型必须和T兼容,而A1的m1是boolean,和B的m1: string不兼容,会触发非预期的报错。
需要触发报错的场景
type D1 = { z: string }; // 传入Z为D1,新增了B不存在的属性z,需要触发类型报错 type Err = SafeRetype<B, D1>;
解决方案
问题出在两处:
- 原约束
Z extends T过度限制了Z的属性类型,我们只需要约束Z的属性名是T的属性名的子集,不需要匹配属性类型 - 需要确保Z如果是联合类型,每个成员都符合属性名的约束
修正后的实现:
type SafeRetype<T, Z extends Partial<Record<keyof T, unknown>>> = { [K in keyof T]?: K extends keyof Z ? Z[K] : undefined; };
效果验证
- 传入
Z = A1 | A2时:- 约束校验通过:A1、A2的属性都是
m1/m2,属于B的属性集合 - 最终生成的类型为
{ m1?: boolean; m2?: number },符合自定义属性类型的需求(原示例注释中写的{ m1?: string; m2?: string }属于笔误)
- 约束校验通过:A1、A2的属性都是
- 传入
Z = D1时:- 约束校验失败:D1的属性
z不属于B的属性集合,触发类型报错,符合预期
- 约束校验失败:D1的属性
内容的提问来源于stack exchange,提问作者Patrick Desjardins
相关产品推荐
相关产品推荐

