我正在尝试根据实例成员的类型来缩小联合范围。如果我正在检查属性的返回类型,但似乎不适用于函数。
这是TypeScript中的限制/错误吗?还是我做错了什么?
谢谢!
enum Types {
A,
B
}
class A {
public get type(): Types.A { return Types.A; }
public getType(): Types.A { return Types.A; }
public getValue(): undefined { return undefined; }
}
class B {
public get type(): Types.B { return Types.B; }
public getType(): Types.B { return Types.B; }
public getValue(): string { return 'string'; }
}
type Classes = A | B;
const c = new A() as Classes;
if (c.getType() === Types.A) {
const v = c.getValue(); // typeof v: string | undefined (can't be inferred?)
}
if (c.getType() === Types.B) {
const v = c.getValue(); // typeof v: string | undefined (can't be inferred?)
}
if (c.type === Types.A) {
const v = c.getValue(); // typeof v: undefined (correct!)
}
if (c.type === Types.B) {
const v = c.getValue(); // typeof v: string (correct!)
}
答案 0 :(得分:1)
这只是对TypeScript执行控制流类型分析的功能的一般限制。对您来说显而易见的是,c.getType()
等同于读取type
上的c
属性,并且对c
的类型具有相同的含义,但是编译器没有意识到这一点。 。只有非常特殊的情况才会在TypeScript中触发type guarding。
一个是检查直接属性读取,例如c.type === Types.A
(其中包括property reads implemented as getters)。您无法将其重构为c.getType()
之类的函数/方法,并且效果会自动从该函数中传播出去。这是hard problem to solve;您可以想象尝试进行inline调用,以便将c.getType()
转换为c.type
以便进行控制流分析,但是这样做会很快变得非常昂贵。
实现类型保护的另一种方法是使用user-defined type guard。这是一种特殊的方法/函数类型,它返回一个boolean
值,该值声明为对调用该方法的对象的类型或其参数之一有影响。不幸的是getType()
不会返回boolean
,因此没有直接的方法将getType()
转换成这种类型的保护。
您可以通过以下方式将其添加到A
和B
中:
public hasType<T extends Types>(t: T): this is Extract<Classes, { type: T }> {
return this.type === t;
}
然后您可以这样称呼它:
if (c.hasType(Types.A)) {
const v = c.getValue(); // undefined
}
if (c.hasType(Types.B)) {
const v = c.getValue(); // string
}
但这对于您要实现的目标可能会更好,也可能不会更好(如果我沿那条路线走,我可能会将hasType()
放在某些超类中或作为独立的类型保护函数)。
好的,希望能有所帮助;祝你好运!