【问题标题】:Narrow union type based on type of instance member基于实例成员类型的窄联合类型
【发布时间】:2019-11-21 03:15:53
【问题描述】:

我正在尝试根据实例成员的类型缩小联合范围。如果我正在检查属性的返回类型,它似乎工作正常,但不适用于函数。

这是 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!)
}

Playground Link

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这只是 TypeScript 执行控制流类型分析功能的一般限制。很明显,c.getType() 等同于读取c 上的type 属性,并且对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() 转换成这样的类型保护。

    您可以这样做,将以下方法添加到AB

    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() 放在某个超类中或作为独立的类型保护函数)。

    好的,希望对您有所帮助;祝你好运!

    Link to code

    【讨论】:

    • 很详细的解释!谢谢:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-27
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-23
    • 2016-01-01
    相关资源
    最近更新 更多