如何使用类通用来确定条件类型的类型?

下面是一个基本示例和TSplayground 链接.

如何在不使用as或任何其他手动更正的情况下拨打this.bthis.a

type X<T> = T extends true ? number : string;

export class A<UN extends boolean, I extends X<UN>> {
  useNumber: UN;

  constructor(useNumber: UN) {
    this.useNumber = useNumber;
  }

  a(input: string): string {
    return `${input}j`;
  }

  b(input: number): number {
    return input * 2;
  }

  c(input: I): string {
    // I expected `UN` generic type (`this.useNumber`) is used as a type guard below.
    //.          =.      UN       ?       I       :       I
    //           =       UN       ?    X<true>    :    X<false>
    //           =       UN       ?    number     :    string
    const result = this.useNumber ? this.b(input) : this.a(input);
    return result.toString();
  }
}

TypeScript Playground

推荐答案

目前这是不可能的; TypScript不会直接修改generic types(例如UN)来响应判断通用values(例如this.useNumber). 换句话说,TypScript不能使用control flow analysis来影响通用类型参数. 在microsoft/TypeScript#33014处有一个开放功能请求来支持这一点,甚至可能很快就会实现(在TypScript 5.5路由图中的microsoft/TypeScript#57475处提到了这一点),但目前它还不能以这种方式工作.

因此,在this.useNumber ? this.b(input) : this.a(input)中,虽然useNumber在真分支中是true,在假分支中是false,但UN顽固地保持不变,不能被限制为truefalse.所以I仍然悬而未决,而且不起作用.


到目前为止,最简单的修复方法是使用type assertions或类似的方法来推动通过.

如果您想说服TypScript遵循该逻辑,您主要必须证明thisdiscriminated union,其中useNumber是区分式.也许是这样的:

c(
  this: { useNumber: true, b(input: X<UN>): number } | 
        { useNumber: false, a(input: X<UN>): string },
  input: X<UN>) {
  const result = this.useNumber ? this.b(input) : this.a(input);
  return result.toString();
}

我使用this parameter来告诉编译器,c方法只能用于指定的区分联合类型的实例. 一旦UN被指定为truefalse,编译器确实可以验证这一点,因此以下调用有效:

const n = new A(true);
n.c(3); // okay

const s = new A(false);
s.c("abc"); // okay

这很有效,但它根本不是惯用的TypScript.它正在与语言作斗争.阶级声明不能被歧视unions .对类进行此操作的更惯用的方法是使用继承进行多态性,并在需要时创建子类的联合:

abstract class ABase {
  a(input: string): string {
    return `${input}j`;
  }

  b(input: number): number {
    return input * 2;
  }
}

class ATrue extends ABase {
  readonly useNumber = true;
  c(input: number) {
    return this.b(input).toString();
  }
}

class AFalse extends ABase {
  readonly useNumber = false;
  c(input: string) {
    return this.a(input).toString();
  }
}

type A = ATrue | AFalse;

let a: A;

a = new ATrue();
a.c(3); // okay

a = new AFalse();
a.c("abc"); // okay

因此,这取决于您想要如何继续进行的用例.如果您需要将此行为表示为单个类,那么您要么需要跳过奇怪的问题,要么只需使用类型断言.

Playground link to code

Typescript相关问答推荐

Typescript:将内部函数参数推断为子对象键的类型

使用前的组件渲染状态更新

使某些类型的字段变为可选字段'

如何在排版中正确键入中间件链和控制器链

typescribe警告,explate—deps,useEffect依赖项列表

如何在单击停止录制按钮后停用摄像机?(使用React.js和Reaction-Media-Recorder)

使用TypeScrip根据(可选)属性推断结果类型

记录键的泛型类型

如何在NX工作区项目.json中设置类似angular.json的两个项目构建配置?

在React中定义props 的不同方式.FunctionalComponent

在Cypress中,是否可以在不标识错误的情况下对元素调用.Click()方法?

扩展对象的此内容(&Q;)

如何在Nextjs路由处理程序中指定响应正文的类型

如何在Angular /字体中访问API响应的子元素?

如何在Vue动态类的上下文中解释错误TS2345?

Svelte+EsBuild+Deno-未捕获类型错误:无法读取未定义的属性(读取';$$';)

两个接口的TypeScript并集,其中一个是可选的

我可以使用对象属性的名称作为对象中的字符串值吗?

数据库中的表请求返回如下日期:2023-07-20T21:39:00.000Z 我需要将此数据格式化为 dd/MM/yyyy HH:mm

如何按成员资格排除或 Select 元组?