typescript 如何提取数组中的接口并合并为一个联合

5q4ezhmt  于 2022-12-24  发布在  TypeScript
关注(0)|答案(2)|浏览(168)

真实的的例子是,我想实现一个函数,通过输入一个对象数组来返回一个包含输入数组中所有属性的对象。

function unit<T extends object>(arr: T[]) {
  let obj = {};
  arr.forEach((val) => {
    obj = { ...obj, ...val };
  });
  return obj;
}

const a = { name: "l" };
const b = { age: 1 };

const person = unit([a, b]);

我希望person是一个基于输入的动态类型脚本值,但是我不知道如何实现它。

7cwmlq89

7cwmlq891#

TypeScript不会对循环中的类型变化进行建模,因此编译器不可能计算出obj的结果类型,您必须显式地告诉它您期望的类型。
所以,我们期望什么?好吧,我提议

const person = unit([a, b]);

应该导致personab类型的交集:

// const person: { name: string; } & { age: number; }

注意,如果arr的不同元素有冲突的属性类型,这就不正确了。如果你写unit([{a: 0}, {a: ""}]),结果值就不是{a: number} & {a: string}类型。它并不与它们相交。尽管如此,这已经足够接近了,而且当你把泛型的东西扩展到一起时,编译器通常会这样做:

function spread<T extends object, U extends object>(t: T, u: U) {
  const v = { ...t, ...u };
  // const v: T & U
}

这就是我们的目标:产生arr的每个元素的类型的交集。
首先,您应该在arr的元组类型中创建unit()函数generic,如下所示:

function unit<T extends object[]>(arr: [...T]) {

(其中[...T]是一个可变元组类型,它向编译器提供提示,如果可能,我们希望T是元组类型)
否则,如果在arr的 * element types * 中将其保留为泛型,那么类型参数T将最终成为每个元素类型的并集,这很难分解为它的组成部分,例如,检查personnotPerson之间的差异:

const notPerson = unit([Math.random() < 0.5 ? a : b]);
// const notPerson: { name: string; } | { age: number; }

在这两种情况下,在您的版本中,T都将被推断为{name: string} | {age: number}。但是person应该是{name: string} & {age: number}类型,而notPerson应该是{name: string} | {age: number}类型。为了保持它们的独立性,我们不仅需要关注所有元素类型的并集,还需要关注每个单独的元素类型。
好的,那么,如果T是一个元组,我们如何将它转换成它的元素类型的交集呢?

type TupleToIntersection<T extends any[]> =
  { [I in keyof T]: (x: T[I]) => void }[number] extends
  (x: infer I) => void ? I : never;

它使用条件类型推断从逆变类型位置将并集转换为交集,技术与Transform union type to intersection type的工作方式类似。
我们可以Assertobj的类型为TupleToIntersection<T>

let obj = {} as TupleToIntersection<T>;

但是如果我们这样做的话,编译器会忘记obj是类对象的,当你扩展它的时候会很生气,所以让我们使用Extract<T, U>实用程序类型来提醒它:

let obj = {} as Extract<TupleToIntersection<T>, object>;

这与TupleToIntersection<T>相同,只要它确实是类对象的而不是原语的。
好吧,我们来测试一下:

const person = unit([a, b]);
// const person: { name: string; } & { age: number; }
const notPerson = unit([Math.random() < 0.5 ? a : b]);
// const notPerson: { name: string; } | { age: number; }
const anotherThing = unit([{ a: 1 }, { b: 2 }, { c: 3 }, { d: true }, { e: "" }]);
// const anotherThing: { a: number; } & { b: number; } & { c: number; } & 
//  { d: boolean; } & { e: string; }

看起来不错!
Playground代码链接

hiz5n14c

hiz5n14c2#

这就是你要找的吗?

type A = { [key: string]: string | number }

type Arr = A[]

const test3: Arr = [{a: "a", b: 1}]

console.log(test3)

相关问题