如何在带有泛型的 TypeScript 类中使用属性作为类型保护?

问题描述 投票:0回答:1

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

下面是一个准系统示例和 TS Playground 链接。

如何在不使用

this.b
或任何其他手动更正的情况下调用
this.a
as

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 游乐场

typescript
1个回答
0
投票

目前这还不能直接实现; TypeScript 不会直接修改 generic types(如

UN
)来响应检查通用 values(如
this.useNumber
)。换句话说,TypeScript 无法使用“控制流分析”来影响泛型类型参数。 microsoft/TypeScript#33014 有一个开放的功能请求来支持这一点,甚至可能很快就会实现(在 microsoft/TypeScript#57475 的 TypeScript 5.5 路线图中提到过),但目前还没有就这样工作。 因此,在

this.useNumber ? this.b(input) : this.a(input)

中,虽然

useNumber
在真分支中是
true
,在假分支中是
false
,但
UN
顽固地保持不变,不能被限制为
true
false
。所以
I
仍然悬而未决,它不起作用。

到目前为止,最简单的解决方法是使用
类型断言

或类似的东西来推动。 如果你想说服 TypeScript 遵循逻辑,你主要必须证明

this

是一个

受歧视联合
,其中 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 参数

 来告诉编译器 
c 方法只能用于指定的可区分联合类型的实例。一旦
UN
被指定为
true
false
,编译器确实可以验证这一点,因此以下调用有效:
const n = new A(true);
n.c(3); // okay

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

这可行,但它根本不是惯用的 TypeScript。这是与语言作斗争。类声明不能是可区分的联合。对类执行此操作的更惯用方法是使用继承来实现多态性,并在需要时对子类进行联合:

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 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.