TypeScript中如何约束prop参数以访问对象的addressSeq属性
TypeScript类型约束问题:限制函数参数为指定值类型的键
问题场景
现有以下TypeScript代码:
interface IpPatientAddressDto { ... addressSeq: number; } interface IpPatientInfoDto { ... localAddress: IpPatientAddressDto; } const originalPatient:IpPatientInfoDto = { ... localAddress:{ addressSeq: 1 } } const createAddrCollectionObj = (prop: keyof IpPatientInfoDto) => { return { addressSeq: originalPatient[prop].addressSeq, // 报错TS2339 } } const a1 = createAddrCollectionObj('localAddress');
运行触发TS2339错误:属性“addressSeq”不存在于类型string | number | Date | IpPatientAddressDto | IpPatientRelationshipDto[]上,因为keyof IpPatientInfoDto包含了很多值类型不是IpPatientAddressDto的键,TypeScript无法确定originalPatient[prop]一定有addressSeq属性。
不想使用额外的取值函数写法:
const get = <T extends keyof ObjType>(key: T): ObjType[T] => { return obj[key] } let res = get("a"); //"1"
期望实现:
- 约束
prop参数只能是IpPatientInfoDto中值类型为IpPatientAddressDto的键 - 调用时TypeScript能自动校验传入的键是否合法
- 函数内部能安全访问
addressSeq等IpPatientAddressDto的属性
目标函数示例:
const createAddrCollectionObj = (prop: keyof IpPatientInfoDto) => { const property = originalPatient[prop]; if (!isIpPatientAddressDto(property,prop)) { return null; } return { addressSeq: property.addressSeq, patientSeq: property.patientSeq, address: patInfo[prop + 'Details'], provinceCode: patInfo[prop][0].code, provinceName: patInfo[prop][0].name, cityCode: patInfo[prop][1].code, cityName: patInfo[prop][1].name, areaCode: patInfo[prop][2].code, areaName: patInfo[prop][2].name, zipCode: '', // 邮编 }; };
解决方案
1. 提取目标键类型
先定义一个类型工具,自动筛选IpPatientInfoDto中值类型为IpPatientAddressDto的键:
// 筛选出IpPatientInfoDto中值类型是IpPatientAddressDto的键 type AddressKeysOfPatient = { [K in keyof IpPatientInfoDto]: IpPatientInfoDto[K] extends IpPatientAddressDto ? K : never }[keyof IpPatientInfoDto];
这个类型会遍历IpPatientInfoDto的所有键,只保留符合条件的键(比如你的localAddress)。
2. 约束函数参数
把prop的类型从keyof IpPatientInfoDto替换为AddressKeysOfPatient,这样调用时只能传入合法的键,非法键会直接编译报错:
const createAddrCollectionObj = (prop: AddressKeysOfPatient) => { const property = originalPatient[prop]; // 此时TypeScript已推断property为IpPatientAddressDto,可直接访问addressSeq return { addressSeq: property.addressSeq, patientSeq: property.patientSeq, // 其他属性... }; }; // 合法调用 const a1 = createAddrCollectionObj('localAddress'); // 非法调用会直接编译报错(假设IpPatientInfoDto有name: string字段) // const a2 = createAddrCollectionObj('name');
3. 配合类型守卫处理可选/联合类型
如果IpPatientInfoDto中部分键的类型是IpPatientAddressDto | undefined这类联合类型,需要添加类型守卫确认类型:
// 实现类型守卫函数 function isIpPatientAddressDto(obj: unknown): obj is IpPatientAddressDto { return typeof obj === 'object' && obj !== null && 'addressSeq' in obj; } // 函数中使用类型守卫 const createAddrCollectionObj = (prop: AddressKeysOfPatient) => { const property = originalPatient[prop]; if (!isIpPatientAddressDto(property)) { return null; } return { addressSeq: property.addressSeq, patientSeq: property.patientSeq, // 其他属性... }; };
4. 处理字符串拼接的键(如prop + 'Details')
如果需要访问patInfo[prop + 'Details']这类拼接后的键,需要扩展类型工具确保拼接后的键合法:
// 假设patInfo的类型为PatientDetailsInfo type AddressKeysWithDetails = { [K in keyof IpPatientInfoDto]: IpPatientInfoDto[K] extends IpPatientAddressDto ? `${K}Details` extends keyof PatientDetailsInfo ? K : never : never }[keyof IpPatientInfoDto]; // 使用新的类型约束prop const createAddrCollectionObj = (prop: AddressKeysWithDetails) => { const property = originalPatient[prop]; if (!isIpPatientAddressDto(property)) { return null; } return { addressSeq: property.addressSeq, address: patInfo[`${prop}Details`], // TypeScript可识别此键合法 // 其他地址相关属性... }; };
内容的提问来源于stack exchange,提问作者ZZZxw
相关产品推荐
相关产品推荐

