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

能否通过返回值类型动态映射对象类型并实现类型推断?

TypeScript 对象值映射的类型推断优化

需求

需要将类型 A 的对象转换为类型 B:

type A = { 
  name: string; 
  age: number; 
  dictionary: { type: 'Dictionary'; id: string; name: string } 
}

type B = { 
  name: string; 
  age: number; 
  dictionary: { label: string; value: string } 
}

现有问题

使用 Lodash mapValues 实现的转换逻辑无法让 TypeScript 正确推断输入输出类型,导致代码没有类型提示,返回值类型不符合预期:

type Dictionary = { type: 'Dictionary'; id: string; name: string };

const mapDictionary = ({ id, name }: Dictionary) => ({ label: name, value: id });

// 原代码存在笔误:fieldValue应为value
const cb = (value: string | boolean | number | null | Dictionary) => {
  if (typeof value === 'string') return value;
  if (typeof value === 'boolean') return value;
  if (typeof value === 'number') return value;
  if (value === null) return null;
  if ('type' in value) return mapDictionary(value);
  return value;
};

const mapData = (data) => {
  if (!data) return {};
  return mapValues(data, cb);
};

解决方案

通过泛型类型映射+泛型函数实现自动类型推断,同时保留原对象的键结构:

1. 定义值转换的泛型类型

先声明一个泛型类型,描述单个值的转换规则:

type MapValueType<T> = 
  T extends string ? string :
  T extends number ? number :
  T extends boolean ? boolean :
  T extends null ? null :
  T extends Dictionary ? { label: string; value: string } :
  T;

2. 定义对象级别的转换泛型

遍历原对象的所有键,为每个值应用上述转换规则:

type MapObject<T> = {
  [K in keyof T]: MapValueType<T[K]>;
};

3. 改造为泛型工具函数

给 mapData 和回调函数添加泛型约束,让 TypeScript 自动推断类型:

import { mapValues } from 'lodash';

type Dictionary = { type: 'Dictionary'; id: string; name: string };

type MapValueType<T> = 
  T extends string ? string :
  T extends number ? number :
  T extends boolean ? boolean :
  T extends null ? null :
  T extends Dictionary ? { label: string; value: string } :
  T;

type MapObject<T> = {
  [K in keyof T]: MapValueType<T[K]>;
};

const mapDictionary = ({ id, name }: Dictionary) => ({ label: name, value: id });

const cb = <T>(value: T): MapValueType<T> => {
  // 合并基础类型判断
  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
    return value as MapValueType<T>;
  }
  if (value === null) {
    return null as MapValueType<T>;
  }
  // 更精确的类型守卫,确保是Dictionary类型
  if ('type' in value && value.type === 'Dictionary') {
    return mapDictionary(value) as MapValueType<T>;
  }
  return value as MapValueType<T>;
};

const mapData = <T extends Record<string, any>>(data: T | null | undefined): MapObject<T> => {
  if (!data) {
    return {} as MapObject<T>;
  }
  return mapValues(data, cb) as MapObject<T>;
};

验证效果

现在调用 mapData 时,TypeScript 会自动推断输入对象的类型,并返回正确的映射后类型:

const input: A = {
  name: 'Alice',
  age: 30,
  dictionary: { type: 'Dictionary', id: 'dict_001', name: '测试字典' }
};

const output = mapData(input);
// output的类型会被自动推断为B,拥有完整的类型提示

内容的提问来源于stack exchange,提问作者Bruce Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:05:34