javascript - Typescript - 从数组项的通用类型创建组合类型
问题描述
给定一个用泛型类型实现特定类的项数组,我想从数组项中创建组合类型 - 对于每个项,取其泛型(例如定义为第一个泛型参数)并创建一个实现所有项目的通用值。
例如:
// 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;
}
这是不安全的。您可以在这里找到更多信息
我只是说开发时间对类型的需求
考虑这个例子:
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 的角度来看,这些类型是相等的。
推荐阅读
- javascript - 关于使用 createAysncThunk 的问题
- python - 从头开始实施 VGG16 时出现内存不足错误
- python - 乘以不同的数据框列标签/索引长度
- node.js - 将 expressjs 添加到自定义 reactjs 应用程序
- shell - 如何使用特定字符串从过去 10 分钟的日志文件中获取行
- algorithm - 修改后的皇后问题的布尔表达式
- go - 如何确保按通道生产和消费数据是安全的
- java - Kotlin 等价于 Java `? 扩展`
- r - 在数字和 NA 列表中获取元素权重
- cluster-analysis - 如何在 Sklearn 中使用带有混合(分类和数字)数据的 silhouette_score?