我有一些类,这些类的构造函数需要形成标记联合的对象:
interface Info {
x: string;
}
interface AInfo extends Info {
x: 'a';
}
class A {
constructor(info: AInfo) {}
}
interface BInfo extends Info {
x: 'b';
}
class B {
constructor(info: BInfo) {}
}
type AllInfos = AInfo|BInfo;
我省略了类内部和构造函数初始化。如何键入该工厂函数以关联基于传入信息创建的对象的类型?
const factory = (info: AllInfos) => {
switch (info.x) {
case 'a': return new A(info);
case 'b': return new B(info);
// ...
}
};
const obj = factory(myInfo); // type of obj is A|B, I'd like it to vary by myInfo
答案 0 :(得分:0)
最简单的方法是使用重载:
function factory(info: AInfo): A
function factory(info: BInfo): B
function factory(info: AllInfos) {
switch (info.x) {
case 'a': return new A(info);
case 'b': return new B(info);
// ...
}
};
const objA = factory({ x: 'a' }); // A
const objB = factory({ x: 'b' }); // B
如果联合中有很多选择,则可以发挥创造力并创建分布式条件类型,以为联合中的每个成员创建一个签名,然后使用UnionToIntersection
将其变为重载签名(参见here):
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
type Factory<T> = T extends new (p: infer P) => infer R ? (a: P) => R : never
type FactoryParameters<T> = T extends new (p: infer P) => infer R ? P : never
type AllClasses = typeof A | typeof B
const factory = ((info: FactoryParameters<AllClasses>) => {
switch (info.x) {
case 'a': return new A(info);
case 'b': return new B(info);
}
}) as UnionToIntersection<Factory<AllClasses>>;
const objA = factory({ x: 'a' }); // A
const objB = factory({ x: 'b' }); // B