简体   繁体   English

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

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

Given an array of items that implemented a specific class with a generic type, I would like to create combined type from the array items - For each item, take its generic (defined as first generic argument for example) and create a type that implements all the the generic values of the items.给定一个使用泛型类型实现特定类的项目数组,我想从数组项目创建组合类型 - 对于每个项目,取其泛型(例如定义为第一个泛型参数)并创建一个实现所有类型的类型项目的通用值。

For Example:例如:

// 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

I've tried achieving it in few different ways, can fetch the generic values of the array but failed to achieve the aggregated values of the array.我尝试以几种不同的方式实现它,可以获取数组的通用值但未能实现数组的聚合值。

Its something conceptually similar to this (without the additional depth created due to the of the iteration of the array items):它在概念上与此类似(没有由于数组项的迭代而创建的额外深度):

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;
  };
};

Thanks alot!非常感谢!

Description is in comments说明在评论中

// 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

Playground 操场

PS Avoid declaring bivariant methods: PS避免声明双变量方法:

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

This is unsafe.这是不安全的。 More information you can find here您可以在此处找到更多信息

I just talk about the need of the types for the development time我只说开发时间对types的需求

Consider this example:考虑这个例子:

class BaseItem<T>{ }

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

Since T generic type is unused in BaseItem class implementation, it is completely ignored in class Item1 .由于T泛型类型在BaseItem类实现中未使用,因此它在类Item1被完全忽略。

TS is unable to distinguish BaseItem<ObjectWithMethods1> and BaseItem<ObjectWithMethods2> . TS 无法区分BaseItem<ObjectWithMethods1>BaseItem<ObjectWithMethods2>

See example:见示例:

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

Hence, these types from TS perspective are equal.因此,从 TS 的角度来看,这些类型是相等的。

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

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