typescript 将对象数组Map到函数参数中的对象类型

wwwo4jvm  于 2023-02-05  发布在  TypeScript
关注(0)|答案(1)|浏览(249)

我有这样的函数定义:

function example<
  O extends { property: P; required: boolean },
  P extends string
>(
  arr: O[]
): {
  [P in O["property"]]: O["required"] extends true
    ? string
    : string | undefined;
};

example([
  { property: "hey", required: true },
  { property: "ho", required: false },
]);

它给出了以下类型:

function example<{
    property: "hey";
    required: true;
} | {
    property: "ho";
    required: false;
}, string>(arr: ({
    property: "hey";
    required: true;
} | {
    property: "ho";
    required: false;
})[]): {
    hey: string | undefined;
    ho: string | undefined;
}

required: true应该表示返回的对象肯定有关联的属性,required: false应该表示可能有也可能没有,即string | undefined
因此,hey在本场景中应该就是string,因为required就是true
如果required对于两个键都是true,则它将它们都正确地键入为string,但是如果其中一个键是false,则它似乎扩大了每个键/值的类型。
可以用这种方式单独Map类型吗?
Playground示例。

yduiuuwa

yduiuuwa1#

问题是

{
  [P in O["property"]]: O["required"] extends true
    ? string
    : string | undefined;
}

O很可能是一个并集,因此O["property"]O["required"]将是分离的不相关并集,如果O{ property: "hey"; required: true } | { property: "ho"; required: false },则O["property"]"hey" | "ho",并且O["required"]true | false,并且每个并集的片段之间的任何关联都已经丢失。
另一种解决问题的方法是,Map类型的属性值类型根本没有提到键类型参数P,因此输出不可能具有依赖于单个键的属性值类型。
解决这个问题的一个方法是继续在O["property"]上迭代P,但是在从O获取required属性之前,根据PO过滤到合适的成员。我们可以使用Extract<T, U>实用程序类型来过滤联合体,方法如下:

{
  [P in O["property"]]: Extract<O, { property: P }>["required"] extends true
  ? string
  : string | undefined;
};

结果是

const result = example([
  { property: "hey", required: true },
  { property: "ho", required: false },
]);
/* const result: {
  hey: string;
  ho: string | undefined;
} */

如所期望的。
另一种解决方法是在Map类型中使用键重Map,它允许您在任何联合体上迭代类型参数,然后将键更改为联合体中每个成员的函数。

{
  [T in O as T["property"]]: T["required"] extends true
  ? string
  : string | undefined;
};

因此,我们不用在每个property上迭代P,然后必须 * 找到 * 正确的required,而是在O的每个片段上迭代T,然后只 * 索引 * T,得到propertyrequired,这会得到相同的输出:

const result = example([
  { property: "hey", required: true },
  { property: "ho", required: false },
]);
/* const result: {
  hey: string;
  ho: string | undefined;
} */

Playground代码链接

相关问题