繁体   English   中英

Typescript - 从数组项的通用类型创建组合类型

[英]Typescript - Create Combined Type From Array Items' Generic Types

给定一个使用泛型类型实现特定类的项目数组,我想从数组项目创建组合类型 - 对于每个项目,取其泛型(例如定义为第一个泛型参数)并创建一个实现所有类型的类型项目的通用值。

例如:

// Defining 2 types with different set of methods
type ObjectWithMethods1 = {
  call1(a: string): void;
}

type ObjectWithMethods2 = {
  call2(): number;
}

class BaseItem<T> {
}

// Defining 2 classes that both extends the same class/abstract class,
// each passing a different generic value.
class Item1 implements BaseItem<ObjectWithMethods1> {
}
class Item2 implements BaseItem<ObjectWithMethods2> {
}

// An array that contains instances of the same base class, however each of its items
// used a different type in the BaseItem generics  
const arr: BaseItem<any>[] = [new Item1(), new Item2()]

// How to define the typing of a "getCombinedObject" method that its return type will be an object 
// that implements both ObjectWithMethods1 and ObjectWithMethods2,
// e.g. ObjectWithMethods1 & ObjectWithMethods2
const combined = getCombinedObject(arr);
combined.call1('test'); //void
combined.call2(); //number

我尝试以几种不同的方式实现它,可以获取数组的通用值但未能实现数组的聚合值。

它在概念上与此类似(没有由于数组项的迭代而创建的额外深度):

type CombinedObject<TArray extends TItem[], TItem extends Record<string, any>> = {
  [key: string]: {
    [Index in keyof TArray]: TArray[Index] extends { [key]: any } ? TArray[Index][key] : never;
  };
};

非常感谢!

说明在评论中

// Defining 2 types with different set of methods
type ObjectWithMethods1 = {
    call1: (a: string) => void;
}

type ObjectWithMethods2 = {
    call2: () => number;
}

class BaseItem<T> {
    /**
     * We need to make this class unique
     */
    tag = 'base'
}

/**
 * It is important to extend and implement
 */
class Item1 extends BaseItem<ObjectWithMethods1> implements ObjectWithMethods1 {
    call1 = (a: string) => 'str'
}

/**
 * It is important to extend and implement
 */
class Item2 extends BaseItem<ObjectWithMethods2> implements ObjectWithMethods2 {
    call2 = () => 42;
}

class Item3 { }

// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
    k: infer I
) => void
    ? I
    : never;


/**
 * Elem extends BaseItem<any> - makes sure that each instance extends BaseItem
 * UnionToIntersection - will merge all instances into one
 */
function getCombinedObject<Elem extends BaseItem<any>, Arr extends Elem[]>(arr: [...Arr]): UnionToIntersection<[...Arr][number]>
function getCombinedObject<Elem extends BaseItem<any>, Arr extends Elem[]>(arr: [...Arr]) {
    return arr.reduce((acc, elem) => ({ ...acc, ...elem }), {})
}

const combined = getCombinedObject([new Item1(), new Item2()]);
combined.call1('test'); // string
combined.call2(); // number

操场

PS避免声明双变量方法:

type ObjectWithMethods1 = {
  call1(a: string): void;
}

这是不安全的。 您可以在此处找到更多信息

我只说开发时间对types的需求

考虑这个例子:

class BaseItem<T>{ }

interface ObjectWithMethods1 {
    call1(a: string): void;
}
class Item1 implements BaseItem<ObjectWithMethods1> { }

由于T泛型类型在BaseItem类实现中未使用,因此它在类Item1被完全忽略。

TS 无法区分BaseItem<ObjectWithMethods1>BaseItem<ObjectWithMethods2>

见示例:

declare var x: BaseItem<ObjectWithMethods1>
declare var y: BaseItem<ObjectWithMethods2>
x = y // ok
y = x // ok

因此,从 TS 的角度来看,这些类型是相等的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM