使用枚举类型创建类型多态函数。

huangapple go评论48阅读模式
英文:

Type Polymorphic Functions using Enums

问题

我有一个具有以下签名的工厂方法:

enum FactoryType {
  CAR = 'CAR',
  BIKE = 'BIKE'
}

type CarParamType = {
  cylinders: number
}

type BikeParamType = {
  gears: number
}

factory<F extends FactoryType>(factoryType: F, params: ParamType<F>) {
  switch (factoryType) {
    case FactoryType.CAR:
      return new CarFactory(params);
    case FactoryType.BIKE:
      return new BikeFactory(params);
    default:
       throw new Error('unrecognized factory');
  }
}

BikeFactoryCarFactory 将具有接受各自参数类型的构造函数。在 TypeScript 中,有没有一种方法可以根据枚举来确定 ParamType 的实际类型,以便在工厂方法中尝试将参数传递给它们的构造函数时不会出现编译错误?

我认为 ParamType 可能如下所示:

type ParamType<T> = T extends FactoryType.CAR ? CarParamType
  : T extends FactoryType.BIKE ? BikeParamType : never;

但我认为这里缺少足够的“提示”来为编译器提供所需的信息。

英文:

I have a factory method with the following signature:

enum FactoryType {
  CAR = &#39;CAR&#39;,
  BIKE = &#39;BIKE&#39;
}

type CarParamType = {
  cylinders: number
}

type BikeParamType = {
  gears: number
}

factory&lt;F extends FactoryType&gt;(factoryType: F, params: ParamType&lt;F&gt;) {
  switch (factoryType) {
    case FactoryType.CAR:
      return new CarFactory(params);
    case FactoryType.BIKE:
      return new BikeFactory(params);
    default:
       throw new Error(&#39;unrecognized factory&#39;);
  }

}

Where BikeFactory and CarFactory would have constructors that took in their respective param types.

Is there a way for me to do this in Typescript? That is, using an Enum, determine the actual type of the ParamType is such that I don't get compile errors within the factory method when I try to pass the params into their respective constructors?

I thought ParamType could look something like this:

type ParamType&lt;T&gt; = T extends FactoryType.CAR ? CarParamType
  : T extends FactoryType.BIKE ? BikeParamType : never;

But I don't think there's enough "hints" to give the compiler what it needs.

答案1

得分: 1

以下是您要的翻译部分:

The only supported way to get the narrowing to work with switch/case statements if for factoryType and params to be part of a discriminated union where factoryType is the discriminant property. That requires that they be packaged in a single object, and even though it doesn't look like it, they are. You can think of factory() as having a rest parameter whose type is a discriminated union of tuple types:

唯一支持的使 switch/case 语句与 factoryTypeparams 协同工作的方法是它们必须成为 判别联合 的一部分,其中 factoryType判别属性。这要求它们被打包在一个单一对象中,尽管看起来并不像,但实际上它们是。您可以将 factory() 视为具有判别联合 元组类型剩余参数

function factory(
  ...args:
    [FactoryType.CAR, CarParamType] |
    [FactoryType.BIKE, BikeParamType]
) {
  switch (args[0]) {
    case FactoryType.CAR:
      return new CarFactory(args[1]); // okay
    case FactoryType.BIKE:
      return new BikeFactory(args[1]); // okay
  }
}

That works, but you've lost your factoryType and params names. Luckily, TypeScript also supports destructured discriminated unions, where you destructure into separate variables but the compiler keeps track of the relationship between those variables as if they were properties of the original discriminated union. That brings us to:

这样可以正常工作,但您失去了 factoryTypeparams 的名称。幸运的是,TypeScript 还支持解构的 判别联合,在这里,您将其解构为单独的变量,但编译器会跟踪这些变量之间的关系,就好像它们是原始判别联合的属性。这将我们带到:

function factory(
  ...[factoryType, params]:
    [FactoryType.CAR, CarParamType] |
    [FactoryType.BIKE, BikeParamType]
) {
  switch (factoryType) {
    case FactoryType.CAR:
      return new CarFactory(params); // okay
    case FactoryType.BIKE:
      return new BikeFactory(params); // okay
  }   
}

The code compiles to the slightly weird function factory(...[factoryType, params]) {...} instead of function factory(factoryType, params) {...}, but it acts the same.

这段代码编译成了稍微奇怪的 function factory(...[factoryType, params]) {...},而不是 function factory(factoryType, params) {...},但它的行为是相同的。

Playground link to code

英文:

The only supported way to get the narrowing to work with switch/case statements if for factoryType and params to be part of a discriminated union where factoryType is the discriminant property. That requires that they be packaged in a single object, and even though it doesn't look like it, they are. You can think of factory() as having a rest parameter whose type is a discriminated union of tuple types:

function factory(
  ...args:
    [FactoryType.CAR, CarParamType] |
    [FactoryType.BIKE, BikeParamType]
) {
  switch (args[0]) {
    case FactoryType.CAR:
      return new CarFactory(args[1]); // okay
    case FactoryType.BIKE:
      return new BikeFactory(args[1]); // okay
  }
}

That works, but you've lost your factoryType and params names. Luckily, TypeScript also supports destructured discriminated unions, where you destructure into separate variables but the compiler keeps track of the relationship between those variables as if they were properties of the original discriminated union. That brings us to:

function factory(
  ...[factoryType, params]:
    [FactoryType.CAR, CarParamType] |
    [FactoryType.BIKE, BikeParamType]
) {
  switch (factoryType) {
    case FactoryType.CAR:
      return new CarFactory(params); // okay
    case FactoryType.BIKE:
      return new BikeFactory(params); // okay
  }   
}

The code compiles to the slightly weird function factory(...[factoryType, params]) {...} instead of function factory(factoryType, params) {...}, but it acts the same.

Playground link to code

huangapple
  • 本文由 发表于 2023年2月18日 01:44:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/75487618.html
  • typescript
  • typescript-generics

我可以将 “ :?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定