基于条件动态设置属性并返回对应TypeScript类型的实现方案
解决TypeScript中复用函数处理不同接口类型的类型错误问题
问题背景
需要复用一个函数转换特定类型数组并返回同类型数组,现有两个API生成的接口ResidencePeriodDto和CivilPeriodDto,二者仅状态字段不同(分别为residenceStatus和civilStatus),且无法修改这些类型。尝试通过传入字符串参数动态设置字段实现函数复用时,出现以下TypeScript类型错误:
Type '{ [x: string]: string; dateFrom: string; dateTo: string; source: Source.PRIVATE; }' is not assignable to type 'ResidencePeriodDto | CivilPeriodDto'. Property 'civilStatus' is missing in type '{ [x: string]: string; dateFrom: string; dateTo: string; source: Source.PRIVATE; }' but required in type 'CivilPeriodDto'.
原代码示例:
export interface ResidencePeriodDto { dateFrom?: string; dateTo?: string; residenceStatus: ResidenceStatusType; source: Source; } export interface CivilPeriodDto { dateFrom?: string; dateTo?: string; civilStatus: CivilStatusType; source: Source; } export enum ResidenceStatusType { NOT_REGISTERT_AT_ADDRESS = "NOT_REGISTERT_AT_ADDRESS", REGISTERT_AT_ADDRESS = "REGISTERT_AT_ADDRESS", } export enum CivilStatusType { MARRIED = "MARRIED", NOT_MARRIED = "NOT_MARRIED", } export enum Source { PRIVATE = "PRIVATE", PUBLIC = "PUBLIC", } const testFunc = (period: ResidencePeriodDto | CivilPeriodDto, statusField: "civilStatus" | "residenceStatus"):ResidencePeriodDto | CivilPeriodDto => { return { dateFrom: "01.01.2023", dateTo: "31.12.2023", [statusField]: statusField === "civilStatus" ? CivilStatusType.NOT_MARRIED : ResidenceStatusType.NOT_REGISTERT_AT_ADDRESS, source: Source.PRIVATE } }
解决方案
方法一:泛型+条件类型绑定输入输出
通过泛型约束和条件类型,让statusField参数与返回类型严格关联,确保TypeScript能准确推断返回值类型:
const testFunc = <T extends ResidencePeriodDto | CivilPeriodDto>( period: T, statusField: T extends ResidencePeriodDto ? "residenceStatus" : "civilStatus" ): T => { const baseProps = { dateFrom: "01.01.2023", dateTo: "31.12.2023", source: Source.PRIVATE }; if (statusField === "residenceStatus") { return { ...baseProps, residenceStatus: ResidenceStatusType.NOT_REGISTERT_AT_ADDRESS } as T; } else { return { ...baseProps, civilStatus: CivilStatusType.NOT_MARRIED } as T; } };
方法二:映射类型明确字段与接口对应关系
先定义字段与接口的映射关系,再通过泛型实现类型安全的函数:
// 定义状态字段与对应类型的映射 type StatusFieldMap = { residenceStatus: ResidenceStatusType; civilStatus: CivilStatusType; }; // 根据字段类型推断对应的DTO类型 type PeriodDto<T extends keyof StatusFieldMap> = T extends "residenceStatus" ? ResidencePeriodDto : CivilPeriodDto; const testFunc = <T extends keyof StatusFieldMap>(statusField: T): PeriodDto<T> => { const baseProps = { dateFrom: "01.01.2023", dateTo: "31.12.2023", source: Source.PRIVATE }; return { ...baseProps, [statusField]: statusField === "civilStatus" ? CivilStatusType.NOT_MARRIED : ResidenceStatusType.NOT_REGISTERT_AT_ADDRESS } as PeriodDto<T>; };
错误原因说明
原代码中,TypeScript无法识别动态属性[statusField]对应的具体字段,因此将返回值推断为带有索引签名的通用对象类型,而ResidencePeriodDto和CivilPeriodDto都有必填的专属状态字段,TypeScript认为返回对象可能缺少其中一个字段,从而抛出类型不匹配错误。
内容的提问来源于stack exchange,提问作者Leff
相关产品推荐
相关产品推荐

