基本上我的设置是 我有一个库,其中包含一堆类 A、B、...、Z 所有这些类都共享一个称为 foo 的通用方法,但它们都不是从基类派生的。
这些方法接受参数,数据,其类型为 AT、BT、...、ZT
例如
class B {
foo(data: BT)
}
class Q {
foo(data: QT);
}
在我的代码中,我必须做一些非常重复的事情,归根结底是创建一个方法,该方法接受一个类(例如 A(和一个数据对象(例如 AT( 类型,然后用数据对象在该类对象上调用 foo。
在打字稿中,有没有一种很好的方法,我可以只有一个辅助函数,而不必编写一堆样板代码
即今天我做
createA(data: AT): A {
let a: A;
a.foo(data);
return a;
}
createB(data: BT): B {
let b: B;
b.foo(data);
return b;
}
...
createZ(data: ZT): Z {
let z: Z;
z.foo(data);
return z;
}
我希望能够做
createObject<ClassType, ArgumentType>(data: ArgumentType): ClassType {
let myObj: ClassType;
myObj.foo(data);
return myObj;
}
谢谢
旁注:像let b: B;
这样的代码实际上并没有创建类型B
的对象;在运行时,b
将被undefined
,并且尝试调用其foo()
方法时会遇到直接问题。 我假设/希望在您的实际代码中您实际上是在构造B
等实例。 但这意味着您应该将类构造函数传递给createObject()
函数。 对于其余的答案,我忽略了这一点,并且将或多或少地按原样使用您的签名和实现。 请注意。
你可以像这样编写泛型函数:
function createObject<C extends { foo(a: A): void }, A>(data: A): C {
let myObj: C = null!; // <-- this isn't initialized?
myObj.foo(data);
return myObj;
}
我们使用泛型约束来告诉编译器C
将有一个采用A
类型参数的foo()
方法。 然后,您应该能够调用它:
declare const qt: QT;
const obj = createObject<Q, QT>(qt); // okay, type Q
请注意,如果您希望看到强类型输出,则需要在此处在调用函数时手动指定C
和A
类型参数:
const oops = createObject(qt); //
// const oops: { foo(a: QT): void; }
如果希望编译器从输入类型推断输出类型,则需要createObject()
提供一个调用签名,告诉编译器您关心的每个输入-输出映射:
type IOMap = [QT, Q] | [BT, B] // | ... | [ZT, Z];
type ClassType<A extends IOMap[0]> = Extract<IOMap, [A, any]>[1];
function createObject<A extends IOMap[0]>(data: A): ClassType<A>;
function createObject<C extends { foo(a: A): void }, A>(data: A): C {
let myObj: C = null!; // <-- this isn't initialized?
myObj.foo(data);
return myObj;
}
声明起来有点烦人,但更容易使用:
const obj = createObject(qt); // okay
// const obj: Q
好的,希望有帮助;祝你好运!
操场链接到代码