Reputation: 10657
In the following code, I expect that TypeScript should be able to realize that at the line with the comment, the user
object cannot have any other value for role
expect for admin
.
And it kinda does! If you paste this code into TypeScript Playground, you will notice that hovering over role
at that point does indeed show just admin
!
But still, an error is shown at the return DemoForAdmin
line. And hovering over just user
there, the popup showing the type shows the full union for the role
field.
function Demo(user: { name: string; role: 'user' | 'admin'; } | null) {
if (user === null) {
return 'You have to be logged in.';
}
if (user.role !== 'admin') {
return 'You have to be an admin';
}
user.role; // Hover over role to see "admin"
return DemoForAdmin(user);
}
function DemoForAdmin(user: { name: string; role: 'admin'; }) {
return 'You are an admin';
}
Shouldn't TypeScript be able to realize it is safe (it is, right?) to pass the object to that method and should it be able to infer the correct narrowed string union in both cases of hovering over user
as well as role
, not just role
?
Upvotes: 1
Views: 113
Reputation: 250056
The type guard only applies to the field and only narrows the field, the type guard will not impact the containing object type.
There is another construct that allows you to do this called discriminated union. The type has to be a union of object types with a discriminat of a litearal type. In this case role
can be used. This should work
function Demo(user: { name: string; role: 'user' } | { name: string; role: 'admin'; } | null) {
if (user === null) {
return 'You have to be logged in.';
}
if (user.role !== 'admin') {
return 'You have to be an admin';
}
user.role; // Hover over role to see "admin"
return DemoForAdmin(user);
}
function DemoForAdmin(user: { name: string; role: 'admin'; }) {
return 'You are an admin';
}
You can read more about discriminated unions here
Upvotes: 2