如何使用类泛型来确定条件类型的类型?
下面是一个准系统示例和 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 不会直接修改 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 代码链接