打字稿中的联合类型推断

时间:2019-12-26 14:29:38

标签: typescript

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)

为什么演示在初始化时没有报告错误? 用户定义的类型保护器

2 个答案:

答案 0 :(得分:1)

这是TypeScript中的open issue (microsoft/TypeScript#20863)。您的联合类型不是discriminated union,因此编译器不会在执行excess property checking之前将联合分成成员。大多数人(包括我自己在内)都希望对工会的每个成员都进行多余的财产检查,而不管工会是否是受歧视的工会。不过,目前只是这样:编译器认为"b"是至少一个工会成员中可接受的属性,并决定不抱怨。

请注意,检查多余的属性是一种方便,而不是类型安全问题。 TypeScript中的对象类型为 open ,您可以始终在定义中添加更多属性,而不会违反类型。尽管具有{x: 1, y: 2}属性,值{x: number}仍然是有效的y。换句话说,TypeScript中的对象类型不是exact。因此,从技术上讲,{ a: '123', b: '123' }是有效的p3,因此也是有效的p4。因此,从技术上讲,您不能仅仅检查是否存在b来区分p1p3。是的,如果您只是尝试说const 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
}

好吧,事实证明the in type guard is intentionally unsound。从技术上讲,使用它并不安全,但是人们可以这样做,通常这不是问题。但这对您没有帮助。哦,很好。


那么,您应该在这里做什么?如果您打算测试bp1之间的区别,则您的p3类型应明确说明:

p3

现在type p3 = { a: string, b?: undefined }; // p3 cannot have a defined "b" property 类型是as of TypeScript 3.2+,是一个真正的有区别的联合。所以这是一个错误:

p4

并使不健全的let demo: p4 = { a: '123', b: '123' } // error now 测试显示为错误。如果您想做一个“好”的'b'测试,现在就可以测试b了,它肯定会用新的{{1来区分obj.b !== undefinedp1 }}定义:

p3

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

Link to code

答案 1 :(得分:0)

使用自定义类型保护功能来缩小类型的范围,而不是'b' in obj

    if (!isP3(obj)) {
        obj.a.toFixed() // Error
    } else { 

    }
}

关于作业let demo: p4 = { a: '123', b: '123' },我也很困扰,没有给出错误。如我所知,如果我们将a定义为布尔值而不是数字,它将可以正常工作(这意味着给出错误)。看起来只有分配器类型包含联合类型本身时分配才会失败。您可以订阅此问题以获取详细信息https://github.com/microsoft/TypeScript/issues/35861

有关此问题的规范部分并未完全解释当前的行为。看起来像是一种解释,必须先了解一下编译器本身。 https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#34-union-types