Issue
Why is it that Typescript cannot discriminate a type union whose types are from the return types of function, without explicitly declaring the return type on the function?
Here I do not specify the return values of the event creator functions and the union type cannot be narrowed.
enum EventType {
FOO = "foo",
GOO = "goo",
}
function createFooEvent(args: {
documentId: number | null
}) {
return {
type: EventType.FOO,
data: args
}
}
function createGooEvent(args: {
id: number
isSelected: boolean
}) {
return {
type: EventType.GOO,
data: args
}
}
type EventArgType =
| ReturnType<typeof createFooEvent>
| ReturnType<typeof createGooEvent>
function eventHandler(event: EventArgType) {
switch(event.type) {
case EventType.FOO: {
// Note that `event` contains `data` but `data`'s type is a union and has not been discriminated
event.data;
break
}
}
}
But if I specify the return types as follows then the union can be discriminated.
function createFooEvent(args: {
documentId: number | null
}): {
type: EventType.FOO,
data: {
documentId: number | null
}} {
return {
type: EventType.FOO,
data: args
}
}
function createGooEvent(args: {
id: number
isSelected: boolean
}): {
type: EventType.GOO,
data: {
id: number
isSelected: boolean
}} {
return {
type: EventType.GOO,
data: args
}
}
Here is an example in TS playground.
Solution
Because typescript does not infer the constant as the type by default: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
For example:
var a = 'test'
Typescript will infer type of a as string not as 'test'.
You can fix it using as const:
var a = 'test' as const;
In this case a will be of type 'test'.
Same for your code:
function createFooEvent(args: {
documentId: number | null
}) {
return {
type: EventType.FOO,
data: args
};
}
The return type of the function is {type: EventType} instead of {type:'foo'}.
Adding as const to the return type, will work as you expect TS Playground
function exampleOne(){
enum EventType {
FOO = "foo",
GOO = "goo",
}
function createFooEvent(args: {
documentId: number | null
}) {
return {
type: EventType.FOO,
data: args
} as const;
}
function createGooEvent(args: {
id: number
isSelected: boolean
}) {
return {
type: EventType.GOO,
data: args
} as const;
}
type EventArgType =
| ReturnType<typeof createFooEvent>
| ReturnType<typeof createGooEvent>
function eventHandler(event: EventArgType) {
switch(event.type) {
case EventType.FOO: {
// event.data in this case will be {documentId: number|null}
event.data;
break
}
}
}
}
Answered By - Andrei Tătar
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.