You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Type guard not work in union type of objects #30506
Search Terms:
type guard, union type of objects, typeof
Code
functionfn(i: {foo: string,bar: number,baz: boolean}|{foo: number,bar: boolean}){// Notice: i.foo has two different types in intelligence and after written down.if(typeofi.foo==='string'){// as screenshot, in intelligence, 'i.foo' is of type 'string | number' i.foo// after written down, i.foo becomes 'string'i.bar// always 'number | boolean'}}
Code 2
functionfn(i: {foo: string,bar: number,baz: boolean}|{foo: number,bar: boolean}){// NOTICE: we added parenthesisif((typeofi.foo)==='string'){i.foo// string | numberi.bar// number | boolean}}
Expected behavior:
in 'if' branch, ‘i’ should be { foo: string, bar: number, baz: boolean }
The type of foo inside the if is string so the type guard does work on that field as it should. The code completion entry does not show the flow type (might be the topic of a different issue), but if you try to . into foo you will see it is a string.
Narrowing the parent object is only done in specific scenarios, when the property is considered a discriminant for the union. A property is considered as a discriminant property if:
The a property of a union type to be a discriminant property if it has a union type containing at least one unit type and no instantiable types as outlined here Allow non-unit types in union discriminants #27695
Might be other scenarios that I haven't quoted here, but the scenario in the code is not covered by any as far as I can tell.
As a side note, you can discriminated the union in the example using an in type guard:
Zheeeng (@zheeeng) These cases must be specifically crafted to work, they don't just happen. I am not sure there is a definitive answer as to why more cases are not considered as property discriminants but Anders Hejlsberg (@ahejlsberg) mentions in the original discriminated union PR in a response to a similar request to yours that:
One concern is how this would affect performance. It seems that for every reference to x in a control flow graph we would now have to examine every type guard that has x as a base name in a dotted name. In other words, in order to know the type of x we'd have to look at all type guards for properties of x. That has the potential to generate a lot of work.
Maybe someone in the team can tell us if this is still the definitive reason or there are other concerns.
The lack of narrowing isn't because the type of foo is not a discriminant type; a typeof type guard has never discriminated union types. Are there compelling use-cases where narrowing would be desirable? In my limited experience I've never bumped into this restriction.
... a typeof type guard has never discriminated union types. Are there compelling use-cases where narrowing would be desirable? In my limited experience I've never bumped into this restriction.
As a newbie, this can be confusing. For example, when I look at this stackoverflow question, the code looks like it should compile (to me).
The official documentation has a section for nearly every form of JavaScript "narrowing" I can think of. Then it goes even further, providing TypeScript-only solutions like Type Predicates.
From my perspective, the most compelling reason to implement this is to make the language feel consistent. Currently, typeof not discriminating unions feels like an arbitrary restriction that will surprise most people that run into it for the first time.
Now having said all that, I just wanted to answer your question since nobody else did. In my opinion, this might be more of a documentation issue than a problem with the language. And for that reason, I plan to open a separate issue.
Activity
dragomirtitian commentedon Mar 20, 2019
The type of
fooinside theifisstringso the type guard does work on that field as it should. The code completion entry does not show the flow type (might be the topic of a different issue), but if you try to.intofooyou will see it is astring.Narrowing the parent object is only done in specific scenarios, when the property is considered a discriminant for the union. A property is considered as a discriminant property if:
Might be other scenarios that I haven't quoted here, but the scenario in the code is not covered by any as far as I can tell.
As a side note, you can discriminated the union in the example using an
intype guard:Or use a custom type guard.
zheeeng commentedon Mar 20, 2019
Titian Cernicova-Dragomir (@dragomirtitian) I have a question on why TS doesn't provide us the typeof guarding on codes above? Is there an underlying unsafe situation?
dragomirtitian commentedon Mar 20, 2019
Zheeeng (@zheeeng) These cases must be specifically crafted to work, they don't just happen. I am not sure there is a definitive answer as to why more cases are not considered as property discriminants but Anders Hejlsberg (@ahejlsberg) mentions in the original discriminated union PR in a response to a similar request to yours that:
Maybe someone in the team can tell us if this is still the definitive reason or there are other concerns.
jack-williams commentedon Mar 20, 2019
The lack of narrowing isn't because the type of
foois not a discriminant type; atypeoftype guard has never discriminated union types. Are there compelling use-cases where narrowing would be desirable? In my limited experience I've never bumped into this restriction.DanKaplanSES commentedon Nov 21, 2023
Jack Williams (@jack-williams)
As a newbie, this can be confusing. For example, when I look at this stackoverflow question, the code looks like it should compile (to me).
The official documentation has a section for nearly every form of JavaScript "narrowing" I can think of. Then it goes even further, providing TypeScript-only solutions like Type Predicates.
From my perspective, the most compelling reason to implement this is to make the language feel consistent. Currently,
typeofnot discriminating unions feels like an arbitrary restriction that will surprise most people that run into it for the first time.Now having said all that, I just wanted to answer your question since nobody else did. In my opinion, this might be more of a documentation issue than a problem with the language. And for that reason, I plan to open a separate issue.