【问题标题】:Extend generic class with additional generic parameter使用附加泛型参数扩展泛型类
【发布时间】:2023-02-23 21:20:07
【问题描述】:

我有一个通用基类:

export class BaseSerializer<
  R extends boolean = false,
  M extends boolean = false,
> {
  readonly readonly: R;
  readonly many: M;

  constructor(options: { 
    readonly?: R, 
    many?: Many 
  } = {}) {
    // @ts-ignore
    this.readonly = options?.readonly || false;

    // @ts-ignore
    this.many = options?.many || false;
  }

  public fromDTO = (data: any): any => { return }
  public toDTO = (data: any): any => { return }
}

当我在没有泛型的类中扩展它时,它按预期工作:

export class DateField<
  R extends boolean = false,
  M extends boolean = false,
> extends BaseSerializer<R, M> {
  fromDTO = (data: any) => new Date(data)
  toDTO = (data: any) => new Date(data).toISOString()
}

const serializer = new DateField({ many: true })
typeof serializer.many // true

但是当我扩展到一个带有附加泛型的类时,BaseSerializer 泛型赋值不起作用,R/M 泛型只获得它们的默认值。

export class EnumField<
  T extends any = any,
  R extends boolean = false,
  M extends boolean = false,
> extends BaseSerializer<R, M>{
  fromDTO = (data: any) => data as T
  toDTO = (data: any) => data as T
}

type T = "a" | "b" | "c"
const serializer = new EnumField<T>({ many: true, readonly: true });
// tsafe tests
assert<Equals<typeof serializer["readonly"], true>>() // Type 'false' does not satisfy the constraint 'true'
assert<Equals<typeof serializer["many"], true>>() // Type 'false' does not satisfy the constraint 'true'

您能否建议,当扩展的 EnumField 类从其方法返回 T 泛型值并且同时正确解决 readonlymany 字段时,我如何实现行为?

【问题讨论】:

  • Afaik TS 无法处理我给你第一个通用参数并从传递的参数推断其余参数的这种混合。要么它应该推断出所有的通用类型,要么你必须传递所有这些类型。并且由于您传递了第一个,它不会从传递的对象中推断出另外两个,而是退回到默认值。但我不知道如何解决这个问题。
  • 在这种情况下,替代方案有点奇怪,但我认为最好手动设置每个通用

标签: typescript typescript-typings typescript-generics typescript-class


【解决方案1】:

您可以使用具有第一个通用咖喱的工厂函数来解决这个问题

const createEnumField = <T>() =>
  <R extends boolean, M extends boolean>(options: { readonly?: R, many?: M}) =>
    new EnumField<T, R, M>(options);
    
const serializer = createEnumField<T>()({ many: true, readonly: true });

另一种选择是向构造函数或 options 对象添加一个额外的假运行时参数


const _ = null as any;

export class EnumField<
  T extends any = any,
  R extends boolean = false,
  M extends boolean = false,
> extends BaseSerializer<R, M> {
  fromDTO = (data: any) => data as T
  toDTO = (data: any) => data as T
  
  constructor(options: { readonly?: R, many?: M, returnType?: T} = {}) {
    super(options);
  }
}

type T = "a" | "b" | "c"

const serializer = new EnumField({ many: true, readonly: true, returnType: <T>_ })

【讨论】:

  • 这是一个选择,谢谢!如果可能的话,我更喜欢为所有字段保留相同的接口,所以我开始思考,mb 只传递所有泛型更容易,比如 new EnumField&lt;T, true, true&gt;()({ many: true, readonly: true }); 以保持接口一致,但工厂是一个不错的选择。让我们看看是否会有更接近我想要的其他答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-24
  • 1970-01-01
  • 1970-01-01
  • 2016-09-14
  • 1970-01-01
相关资源
最近更新 更多