[英]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.