typescript - 如何使用条件类型在函数体内进行类型缩小
我定义了一个条件类型,以便我的类构造函数的第二个参数根据第一个参数的类型受到限制。
类型检查器可以正确识别我在调用构造函数时被允许的内容。然而,在我的构造函数内部,我无法让编译器推断类型。
enum Type {
A = 'A',
B = 'B',
}
type TypeKeys<T extends Type> =
T extends Type.A ? {a: number} :
T extends Type.B ? {b: number} : never;
class TestClass<T extends Type> {
constructor(type: T, data: TypeKeys<T>) {
if(type === Type.A) {
data.a // Property 'a' does not exist on type 'TypeKeys<T>'.
}
}
}
new TestClass(Type.A, {a: 10})
//Argument of type '{ a: number; }' is not assignable to parameter of type '{ b: number; }'.
new TestClass(Type.B, {a: 10})
有没有办法在构造函数体内利用类型收窄?
答案#1
一种选择是使用判别联合。在两个接口上添加共享属性(类型为 Type 的 kind)。这样 TypeScript 就可以在构造函数体内和外部对类型进行“判别”。
enum Type {
A = 'A',
B = 'B',
}
interface A {
kind: Type.A;
a: number;
}
interface B {
kind: Type.B;
b: number;
}
class TestClass {
constructor(data: A | B) {
if(data.kind === Type.A) {
data.a = 5;
} else {
data.a // Property 'a' does not exist on type 'TypeKeys<T>'.
}
}
}
new TestClass({ kind: Type.A, a: 10 })
//Argument of type '{ a: number; }' is not assignable to parameter of type '{ b: number; }'.
new TestClass({ kind: Type.B, a: 10 })
答案#2
我猜编译器还不够聪明,无法隐式地判断你的代码是安全的。我认为发生了以下情况:
当你创建类的新实例时,编译器会看到你的类型约束,并正确地阻止你传递错误的数据。然而,在你的构造函数内部,它似乎没有在两个不同的变量之间传播该约束。我想可能是因为如果你有多个变量控制你的数据类型,情况可能会变得非常复杂。
Typescript 确实有一种称为区分联合类型(discriminated unions)的功能,是为像这样的情况设计的,尽管根据个人经验,我发现自动类型推断有时会出错,在某些情况下根本无法工作。我已经放弃让它自动推断类型,所以我经常通过类型转换来使其工作:
if (data.type === Type.A) {
const dataA = <A>data;
dataA.a;
}
