You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于条件动态设置属性并返回对应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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:07:03