我可以通过返回类型动态地将一种对象类型映射到另一种对象类型吗?

问题描述 投票:0回答:1

我想实现动态值映射:

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 地图值:

const mapData = (data) => {
  if (!data) { return {} }

  return mapValues(data, cb)
}

其中回调指的是:

const cb = (value: string | boolean | number | null | Dictionary) => {
  if (typeof fieldValue === 'string') {
    return fieldValue as string;
  }
  if (typeof fieldValue === 'boolean') {
    return fieldValue as boolean;
  }
  if (typeof fieldValue === 'number') {
    return fieldValue as number;
  }
  if (fieldValue === null) {
    return null;
  }

  if (has(fieldValue, 'type')) {
    return mapDictionary(fieldValue)
  }
}
const mapDictionary = ({ id, name }: Dictionary) => ({ label: name, value: id })

问题: mapValues 无法识别输入/输出的内容。

我可以更轻松地以通用方式进行映射吗?它将推断原始输入对象的键,但在映射后具有新的值类型?

typescript lodash
1个回答
0
投票

您可以使用映射类型、条件类型和

infer
关键字来实现此目的。

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

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

type Convert<T extends {}> = {
    [key in keyof T]: ConvertField<T[key]>
}

type ConvertField<T> = T extends (string | number | boolean | null) 
    ? T 
    : T extends {type: string, id: infer ID, name: infer NAME} 
        ? {label: NAME, value: ID}
        : never;

type MyTypeConverted = Convert<MyType>;
type MyTypeMoreSpecificConverted = Convert<MyTypeMoreSpecific>;

您可以在playground上查看此代码。当您将鼠标悬停在转换后的类型上时,您将看到有关它的详细信息。

这是如何工作的:

Convert<T>
接受一个对象类型并返回新的映射对象类型,其中对于原始对象的每个键,我们都有
ConvertField<T>
,我们在其中传递原始值。
ConvertField<T>
依次检查传递的类型是否为字符串、数字、布尔值或 null,如果是,则按原样返回。如果传递的参数是形状为
{type: string, id: <something>, name: <something>}
的对象,那么我们“提取”
id
name
的类型,并使用之前提取的类型返回带有字段
label
value
的新类型。

如果您以前没有使用过条件类型(尤其是

infer
),可能会有点棘手,所以如果需要的话,我建议您查看 TypeScript 手册中的本章

© www.soinside.com 2019 - 2024. All rights reserved.