繁体   English   中英

Static 工厂方法返回通用类型 Class

[英]Static Factory Method Return Type of Generic Class

我正在尝试为一系列类推断 Static 工厂方法的正确类型,其中父类 class 是通用的。 我希望 static 工厂方法的返回类型成为父 class 以抽象两个子类,但 typescript 推断出两个子类的 or 类型。

abstract class Parent<T> {
  abstract readonly property: T
}

class ChildA implements Parent<string> {
  constructor(readonly property: string) {}
}

class ChildB implements Parent<number> {
  constructor(readonly property: number) {}
}

class Factory {
  public static create(guard: any) /** I want to the return type be only Parent without indicate in a explicit way the generic **/ {
    if (typeof guard === 'string') {
      return new ChildA(guard)
    }
    if (typeof guard === 'number') {
      return new ChildB(guard)
    }

    return new UnkwonClass()
  }
}

我不知道如何描述工厂签名以仅返回Parent以抽象两个子类,因为两者将具有相同的形状,并且没有或类型ChildA | ChildB ChildA | ChildB

我尝试将签名写为 Parent,然后 typescript 告诉我,Parent 是通用的,然后我将 create 方法的签名更改为public static create<T>(guard: any): Parent<T>但我有从实例传递类型,我希望 ts 推断我传递给子类的类型。

操场

认为您遇到的问题是 TypeScript 编译器目前无法使用控制流分析来缩小以下代码中的T泛型类型参数

class Factory {
  public static create<T>(guard: T): Parent<T> {

    if (typeof guard === 'string') {
      return new ChildA(guard); // error!
      // Type 'ChildA' is not assignable to type 'Parent<T>'
    }

    if (typeof guard === 'number') {
      return new ChildB(guard); // error!
      // Type 'ChildB' is not assignable to type 'Parent<T>'
    }

    throw new Error('Unknown class')
  }
}

当您检查typeof guard === "string"时,编译器可以将明显的guard类型从T缩小到T & string 但这不会导致编译器说T本身现在是string 类型保护缩小了的类型,而不是泛型类型参数 而且由于T没有缩小到string ,类型Parent<T>没有缩小到Parent<string> ,因此ChildA不被视为可分配给Parent<string>


GitHub 中有各种未解决的问题,要求在此处进行一些改进。 一个好的开始是microsoft/TypeScript#33014 ,它要求编译器通过控制流分析缩小类型参数,至少允许某些属性查找。 从 TypeScript 4.2 开始,该建议和相关建议均未实施......而且尚不清楚何时甚至是否会发生任何变化。


除非有什么改变,否则我的建议是,当你比编译器更了解某个值的类型时,你总是可以做的事情:使用类型断言 知道当typeof guard === "string"ChildA可以分配给Parent<T> ,所以只需告诉编译器:

class Factory {
  public static create<T>(guard: T): Parent<T> {

    if (typeof guard === 'string') {
      return new ChildA(guard) as Parent<typeof guard>; // okay
    }

    if (typeof guard === 'number') {
      return new ChildB(guard) as Parent<typeof guard>; // okay
    }

    throw new Error('Unknown class')
  }
}

这解决了错误。 (请注意,如果编译器没有看到Typetypeof value充分相关,当您将value as Type时,您的实际代码仍可能会产生错误。如果是这样,您仍然可以通过value as unknown as Typevalue as any as Type 。)

请注意类型断言,因为现在您有责任在这些行中验证类型安全性。 编译器无法做到这一点,如果您的断言有误,编译器不会在 position 中引起注意。 不小心对编译器撒谎可能会导致运行时出现奇怪的行为。 因此,在断言之前,请仔细检查new ChildA(guard)new ChildB(guard)是否真的是Parent<T>类型。

Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM