Typescript - 从函数 return 类型缩小类型联合
Typescript - narrowing a type union from function return types
为什么 Typescript 无法区分类型来自 return 类型函数的类型联合,而无需在函数上显式声明 return 类型?
这里我没有指定事件创建函数的return值,联合类型不能缩小。
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
}
}
}
但是如果我如下指定 return 类型,则联合可以被区分。
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
}
}
因为打字稿默认情况下不会将常量推断为类型:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
例如:
var a = 'test'
Typescript 会将 a
的类型推断为 string
而不是 'test'
.
您可以使用 as const
修复它:
var a = 'test' as const;
在这种情况下 a
的类型为 'test'
。
您的代码也一样:
function createFooEvent(args: {
documentId: number | null
}) {
return {
type: EventType.FOO,
data: args
};
}
函数的return类型是{type: EventType}
而不是{type:'foo'}
。
将 as const
添加到 return 类型,将按您预期的方式工作 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
}
}
}
}
为什么 Typescript 无法区分类型来自 return 类型函数的类型联合,而无需在函数上显式声明 return 类型?
这里我没有指定事件创建函数的return值,联合类型不能缩小。
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
}
}
}
但是如果我如下指定 return 类型,则联合可以被区分。
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
}
}
因为打字稿默认情况下不会将常量推断为类型:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
例如:
var a = 'test'
Typescript 会将 a
的类型推断为 string
而不是 'test'
.
您可以使用 as const
修复它:
var a = 'test' as const;
在这种情况下 a
的类型为 'test'
。
您的代码也一样:
function createFooEvent(args: {
documentId: number | null
}) {
return {
type: EventType.FOO,
data: args
};
}
函数的return类型是{type: EventType}
而不是{type:'foo'}
。
将 as const
添加到 return 类型,将按您预期的方式工作 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
}
}
}
}