首页 > 解决方案 > 打字稿中的联合类型推断

问题描述

type p1 = { a: number, b: string }
type p3 = { a: string }
type p4 = p1 | p3

let demo: p4 = { a: '123', b: '123' }

function isP3(obj: p4): obj is p3 { 
    return typeof (<p3>obj).a === 'string'
}

function func(obj: p4) {
    if ('b' in obj) {
        // Uncaught TypeError: obj.a.toFixed is not a function
        obj.a.toFixed() //<- Now, no error is given
    } else { 

    }
}
func(demo)

为什么demo初始化时没有报错?用户定义的类型保护

标签: typescript

解决方案


这是 TypeScript 中的一个未解决问题 (microsoft/TypeScript#20863)。您的 union 类型不是可区分的 union ,因此编译器不会在执行多余的属性检查之前将 union 拆分为成员。大多数人(包括我自己)都希望工会的每个成员都应该进行过多的财产检查,无论工会是否是受歧视的工会。不过,目前情况就是这样:编译器认为这"b"是至少一个联合成员的可接受属性,并决定不抱怨。

请注意,过多的属性检查是一种方便,而不是类型安全问题。TypeScript 中的对象类型是open,您始终可以在定义中添加更多属性而不违反类型。尽管具有该属性,但值{x: 1, y: 2}是有效的。另一种说法是 TypeScript 中的对象类型并不精确。因此,从技术上讲,它是有效的,因此是有效的。因此,从技术上讲,您不能只检查是否存在来区分and 。是的,如果你只是试图说你会收到超额财产警告{x: number}y{ a: '123', b: '123' }p3p4bp1p3const demo: p3 = {a: '123', b: '123'}"b",但正如我所说,这只是一种方便。它很容易被击败:

const demo1 = { a: '123', b: '123' };
const demo2: p3 = demo1; // no error

此时您可能会想:“等等,如果"b"不能正确区分p1p3为什么编译器会认为它在 内部func()?”。好问题:

if ('b' in obj) { // why does the compiler think this narrows obj to p1?
    obj.a.toFixed() // no error, but blows up at runtime
}

好吧,事实证明,类型in保护是故意不健全的。从技术上讲,使用它并不安全,但人们会这样做,而且通常这不是问题。但这对你没有帮助。那好吧。


那么,你应该在这里做什么?如果您的意图是进行b区分p1and的测试p3,那么您的p3类型应该明确说明:

type p3 = { a: string, b?: undefined }; // p3 cannot have a defined "b" property    

现在,从 TypeScript 3.2+ 开始p4,该类型是一个真正的可区分联合。所以这是一个错误:

let demo: p4 = { a: '123', b: '123' } // error now

并使不健全的'b'测试显示为错误。如果你想做一个“好”的b测试,你现在可以测试obj.b !== undefined,这肯定会区分 ap1p3p3定义:

function func(obj: p4) {
    if ('b' in obj) {
        obj.a.toFixed() // error now
    }

    if (obj.b !== undefined) {
        obj.a.toFixed(); // okay
    }
}

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

链接到代码


推荐阅读