简体   繁体   English

打字稿中的联合类型推断

[英]union type inference in 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)

Why the demo did not report an error when initialize?为什么demo初始化时没有报错? User-Defined Type Guards用户定义的类型保护

This is an open issue (microsoft/TypeScript#20863) in TypeScript.这是TypeScript 中的一个未解决的问题 (microsoft/TypeScript#20863) Your union type is not a discriminated union , so the compiler doesn't split the union up into members before performing excess property checking .您的联合类型不是可区分的联合,因此编译器在执行多余的属性检查之前不会将联合拆分为成员。 Most people (myself included) would expect that excess property checks should occur for each member of the union whether or not the union is a discriminated one.大多数人(包括我自己)都希望对工会的每个成员进行额外的财产检查,无论工会是否受到歧视。 For now, though, that's just the way it is: the compiler sees that "b" is an acceptable property in at least one of the union members and decides not to complain.不过,就目前而言,情况就是这样:编译器认为"b"是至少一个联合成员中可接受的属性,并决定不抱怨。

Note that excess property checking is a convenience, and not a matter of type safety.请注意,多余的属性检查是一种方便,而不是类型安全问题。 Object types in TypeScript are open , and you can always add more properties to them over what's in the definition without violating the type. TypeScript 中的对象类型是open ,你总是可以在不违反类型的情况下向它们添加更多的属性而不是定义中的内容。 A value {x: 1, y: 2} is a valid {x: number} despite having that y property.尽管具有y属性,但值{x: 1, y: 2}是有效的{x: number} Another way of saying this is that object types in TypeScript are not exact .另一种说法是 TypeScript 中的对象类型并不精确 It is therefore technically true that { a: '123', b: '123' } is a valid p3 and therefore a valid p4 .因此,技术上正确的是{ a: '123', b: '123' }是一个有效的p3 ,因此也是一个有效的p4 And so, technically, you can't just check for the presence or absence of b to distinguish between p1 and p3 .因此,从技术上讲,您不能仅通过检查b的存在与否来区分p1p3 Yes, if you just try to say const demo: p3 = {a: '123', b: '123'} you'll get an excess property warning on "b" , but this is, as I said, just a convenience.是的,如果你只是想说const demo: p3 = {a: '123', b: '123'}你会在"b"上得到一个过多的属性警告,但这正如我所说,只是为了方便. It is easily defeated:很容易被打败:

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

At this point you might be wondering: "wait, if "b" does not properly distinguish p1 from p3 , why does the compiler think it does inside func() ?".此时您可能想知道:“等等,如果"b"没有正确区分p1p3 ,为什么编译器认为它在func()内部?”。 Good question:好问题:

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

Well, it turns out that the in type guard is intentionally unsound .好吧,事实证明in类型保护是故意不健全的 It's technically not safe to use it, but people do and usually it's not a problem.从技术上讲,使用它是不安全的,但人们会这样做,通常这不是问题。 But it doesn't help you here.但这对你没有帮助。 Oh well.那好吧。


So, what should you do here?那么,你应该在这里做什么? If your intent is to make a test for b distinguish between p1 and p3 , then your p3 type should make that clear:如果您的目的是测试b区分p1p3 ,那么您的p3类型应该清楚地表明:

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

Now the p4 type is, as of TypeScript 3.2+ , a true discriminated union.现在, 从 TypeScript 3.2+ 开始p4类型是一个真正的可区分联合。 And so this is an error:所以这是一个错误:

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

And makes the unsound 'b' test show up as an error.并使不健全的'b'测试显示为错误。 If you want to do a "good" b test, you can now test for obj.b !== undefined , which will definitely distinguish between a p1 and p3 with the new p3 definition:如果你想做一个“好”的b测试,你现在可以测试obj.b !== undefined ,它肯定会用新的p3定义区分p1p3

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

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

Okay, hope that helps;好的,希望有帮助; good luck!祝你好运!

Link to code 代码链接

Use your custom type guard function to narrow types down instead of 'b' in obj使用您的自定义类型保护函数来缩小类型而不是'b' in obj'b' in obj

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

    }
}

About the assignment let demo: p4 = { a: '123', b: '123' } it bothers me too that no error is given.关于作业let demo: p4 = { a: '123', b: '123' }没有给出错误也让我感到困扰。 As I found out it will work properly (which means giving an error) if we define a as a boolean instead of a number.正如我发现的那样,如果我们将a定义为布尔值而不是数字,它将正常工作(这意味着给出错误)。 Looks like the assignment fails only if the discriminator type contains union type itself.看起来只有当鉴别器类型包含联合类型本身时,分配才会失败。 You can subscribe to this issue for details https://github.com/microsoft/TypeScript/issues/35861您可以订阅此问题以获取详细信息https://github.com/microsoft/TypeScript/issues/35861

Section of the spec on the matter doesn't explain fully the current behavior.关于此事的规范部分并未完全解释当前的行为。 Looks like for an explanation one have to peek into the compiler itself.看起来必须要查看编译器本身才能进行解释。 https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#34-union-types https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#34-union-types

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM