TypeScript 泛型工厂函数基于实例参数 return 类型而不重载
TypeScript generic factory function to return type based on instance argument without overloading
我想根据提供给工厂方法的实例参数创建实例。我能够使用重载编写以下代码。 如何使用TypeScript泛型来表达?
class Car {
brand = 'Skoda';
}
class Human {
name = 'Jan';
}
class Garage {
lots = 5;
}
class House {
floors = 2;
}
class NotAllowed {
anything = 'anything';
}
class Selection {
pick(what: Car) : Garage;
pick(what: Human) : House;
pick(what: any) : any {
if (what instanceof Car) {
return new Garage();
} else if (what instanceof Human) {
return new House();
} else {
throw Error('Not supported')
}
}
}
const selection = new Selection();
selection.pick(new Car()).lots; // compiler successfully autocompletes
selection.pick(new Human()).floors // compiler successfully autocompletes
selection.pick(new NotAllowed()); // compiler error
您可以将 pick()
的调用签名更改为 generic method whose parameter is of a generic type constrained to the union of your different desired input types, and whose return type is a conditional type,它根据输入类型选择输出类型:
declare class Selection {
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House;
}
这与您的示例调用的行为相同:
const selection = new Selection();
selection.pick(new Car()).lots; // compiler successfully autocompletes
selection.pick(new Human()).floors // compiler successfully autocompletes
selection.pick(new NotAllowed()); // compiler error
很遗憾,您的实现不会被视为类型安全; T
类型在 pick()
的主体内将是 unspecified,因此编译器不会也无法验证 T
是否可分配给 Car
即使 what instanceof Car
为真:
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House {
if (what instanceof Car) {
return new Garage(); // error!
// Type 'Garage' is not assignable to type 'T extends Car ? Garage : House'
} else if (what instanceof Human) {
return new House(); // error!
// Type 'House' is not assignable to type 'T extends Car ? Garage : House'.
} else {
throw Error('Not supported')
}
}
我不会继续讨论为什么会发生这种情况,而是直接将您引导至 microsoft/TypeScript#33912,相关开放功能请求,以改进语言支持以实现返回条件类型的泛型函数。
现在你需要做一些像 type assertion:
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House {
if (what instanceof Car) {
return new Garage() as T extends Car ? Garage : House;
} else if (what instanceof Human) {
return new House() as T extends Car ? Garage : House;
} else {
throw Error('Not supported')
}
}
或等效地,单个呼号overload:
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House;
pick(what: Car | Human) {
if (what instanceof Car) {
return new Garage();
} else if (what instanceof Human) {
return new House();
} else {
throw Error('Not supported')
}
}
我知道你说过你不想重载,但这通常是我的解决方案,因为在任何地方添加类型断言都比较混乱和乏味。
我想根据提供给工厂方法的实例参数创建实例。我能够使用重载编写以下代码。 如何使用TypeScript泛型来表达?
class Car {
brand = 'Skoda';
}
class Human {
name = 'Jan';
}
class Garage {
lots = 5;
}
class House {
floors = 2;
}
class NotAllowed {
anything = 'anything';
}
class Selection {
pick(what: Car) : Garage;
pick(what: Human) : House;
pick(what: any) : any {
if (what instanceof Car) {
return new Garage();
} else if (what instanceof Human) {
return new House();
} else {
throw Error('Not supported')
}
}
}
const selection = new Selection();
selection.pick(new Car()).lots; // compiler successfully autocompletes
selection.pick(new Human()).floors // compiler successfully autocompletes
selection.pick(new NotAllowed()); // compiler error
您可以将 pick()
的调用签名更改为 generic method whose parameter is of a generic type constrained to the union of your different desired input types, and whose return type is a conditional type,它根据输入类型选择输出类型:
declare class Selection {
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House;
}
这与您的示例调用的行为相同:
const selection = new Selection();
selection.pick(new Car()).lots; // compiler successfully autocompletes
selection.pick(new Human()).floors // compiler successfully autocompletes
selection.pick(new NotAllowed()); // compiler error
很遗憾,您的实现不会被视为类型安全; T
类型在 pick()
的主体内将是 unspecified,因此编译器不会也无法验证 T
是否可分配给 Car
即使 what instanceof Car
为真:
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House {
if (what instanceof Car) {
return new Garage(); // error!
// Type 'Garage' is not assignable to type 'T extends Car ? Garage : House'
} else if (what instanceof Human) {
return new House(); // error!
// Type 'House' is not assignable to type 'T extends Car ? Garage : House'.
} else {
throw Error('Not supported')
}
}
我不会继续讨论为什么会发生这种情况,而是直接将您引导至 microsoft/TypeScript#33912,相关开放功能请求,以改进语言支持以实现返回条件类型的泛型函数。
现在你需要做一些像 type assertion:
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House {
if (what instanceof Car) {
return new Garage() as T extends Car ? Garage : House;
} else if (what instanceof Human) {
return new House() as T extends Car ? Garage : House;
} else {
throw Error('Not supported')
}
}
或等效地,单个呼号overload:
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House;
pick(what: Car | Human) {
if (what instanceof Car) {
return new Garage();
} else if (what instanceof Human) {
return new House();
} else {
throw Error('Not supported')
}
}
我知道你说过你不想重载,但这通常是我的解决方案,因为在任何地方添加类型断言都比较混乱和乏味。