return 类型的调用签名联合缩小函数类型
Narrow function type from union of call signatures by return type
我正在尝试创建一个基本上是调用签名重载的类型,但基于 return 类型而不是参数。
type Callback<T> = (value: T) => void
type CallbackHandler<TResult> = (event: object, cb: Callback<TResult>) => void
type AsyncHandler<TResult> = (event: object) => Promise<TResult>
type Handler<TResult> = CallbackHandler<TResult> | AsyncHandler<TResult>
const cbHandler: Handler<number> = (e1, cb1) => {
// this works
cb1(1)
}
cbHandler({}, () => {})
const asyncHandler: Handler<number> = async (e2) => {
// here e2 is implicitly typed as `any`
// it shouldn't because the function returns Promise<number>, not void
return 1
}
asyncHandler({}) // this should be callable with 1 argument
这可能吗?或者也许这应该作为 TS 错误/功能请求提交?
我认为这是因为 asyncHandler
被明确标记为 Handler<number>
。它的定义不会将其缩小到 AsyncHandler
.
看看下面这个简单的例子:
const foo: object = {bar: 'abc'};
foo.bar; //Property 'bar' does not exist on type 'object'.
这里对于 TS foo 来说只是一个对象。
这里的问题是functions with fewer parameters are assignable to functions with more parameters, and functions returning non-void
are assignable to functions returning void
。这些都是有用且类型安全的有意行为,但它们
有时会令人惊讶并产生令人沮丧的后果。
这意味着以下分配有效:
const f = (event: object) => Promise.resolve(3);
const c: CallbackHandler<number> = f; // okay
const a: AsyncHandler<number> = f; // okay
因此当编译器查看时:
const asyncHandler: Handler<number> = async (e2) => { return 1 }
// we'll ignore this error until later ----> ~~
它看到 asyncHandler
可以分配给 CallbackHandler<number>
和 AsyncHandler<number>
并且不会缩小它。这意味着以下错误符合预期:
asyncHandler({}) // error
如果你想避免这种情况,你应该将 asyncHandler
注释为 AsyncHandler<number>
:
const asyncHandler: AsyncHandler<number> = async (e2) => { return 1 }
asyncHandler({}) // okay
当然,这并不能解释为什么 e2
参数上有一个隐含的 any
。
如果您实现函数类型的联合,您需要接受它们参数的联合(请注意,这是您尝试 调用 函数联合时的双重情况类型,您需要传入它们参数的 交集 ,support for which was implemented in TypeScript 3.3). But instead of the compiler contextually typing 回调参数属于此类联合(即 object | object
或只是 object
在你的情况下),编译器只是放弃上下文类型并回退到 any
.
并且即使您更改定义以便确实发生缩小(而不是 void
return,放一些不兼容的东西),您仍然会遇到相同的错误:
const stillAProblem: ((x: string) => number) | ((x: string, y: number) => string) =
x => 3; // error! implicit any
stillAProblem("").toFixed(); // okay
这似乎是 TypeScript 的设计限制或错误。
microsoft/TypeScript#6357 back in 2016 just says "unions of functions don't give you contextual typing" as if it is a fact of life, which it probably was back then. Since then, microsoft/TypeScript#37580 ran into this and calls it a bug. And microsoft/TypeScript#41213 也 运行 进入这个并称它为错误。所以也许这是一个已知的错误。但我不知道什么时候或是否会得到解决。
如果这是您唯一的问题,您可以做的权宜之计是显式注释 e2
回调参数:
const asyncHandler: Handler<number> = async (e2: object) => { return 1 }
但这当然不能解决上面的原始问题:
asyncHandler({}) // still an error
所以我这里的建议还是把asyncHandler
本身注解成AsyncHandler<number>
就完事了
我正在尝试创建一个基本上是调用签名重载的类型,但基于 return 类型而不是参数。
type Callback<T> = (value: T) => void
type CallbackHandler<TResult> = (event: object, cb: Callback<TResult>) => void
type AsyncHandler<TResult> = (event: object) => Promise<TResult>
type Handler<TResult> = CallbackHandler<TResult> | AsyncHandler<TResult>
const cbHandler: Handler<number> = (e1, cb1) => {
// this works
cb1(1)
}
cbHandler({}, () => {})
const asyncHandler: Handler<number> = async (e2) => {
// here e2 is implicitly typed as `any`
// it shouldn't because the function returns Promise<number>, not void
return 1
}
asyncHandler({}) // this should be callable with 1 argument
这可能吗?或者也许这应该作为 TS 错误/功能请求提交?
我认为这是因为 asyncHandler
被明确标记为 Handler<number>
。它的定义不会将其缩小到 AsyncHandler
.
看看下面这个简单的例子:
const foo: object = {bar: 'abc'};
foo.bar; //Property 'bar' does not exist on type 'object'.
这里对于 TS foo 来说只是一个对象。
这里的问题是functions with fewer parameters are assignable to functions with more parameters, and functions returning non-void
are assignable to functions returning void
。这些都是有用且类型安全的有意行为,但它们
有时会令人惊讶并产生令人沮丧的后果。
这意味着以下分配有效:
const f = (event: object) => Promise.resolve(3);
const c: CallbackHandler<number> = f; // okay
const a: AsyncHandler<number> = f; // okay
因此当编译器查看时:
const asyncHandler: Handler<number> = async (e2) => { return 1 }
// we'll ignore this error until later ----> ~~
它看到 asyncHandler
可以分配给 CallbackHandler<number>
和 AsyncHandler<number>
并且不会缩小它。这意味着以下错误符合预期:
asyncHandler({}) // error
如果你想避免这种情况,你应该将 asyncHandler
注释为 AsyncHandler<number>
:
const asyncHandler: AsyncHandler<number> = async (e2) => { return 1 }
asyncHandler({}) // okay
当然,这并不能解释为什么 e2
参数上有一个隐含的 any
。
如果您实现函数类型的联合,您需要接受它们参数的联合(请注意,这是您尝试 调用 函数联合时的双重情况类型,您需要传入它们参数的 交集 ,support for which was implemented in TypeScript 3.3). But instead of the compiler contextually typing 回调参数属于此类联合(即 object | object
或只是 object
在你的情况下),编译器只是放弃上下文类型并回退到 any
.
并且即使您更改定义以便确实发生缩小(而不是 void
return,放一些不兼容的东西),您仍然会遇到相同的错误:
const stillAProblem: ((x: string) => number) | ((x: string, y: number) => string) =
x => 3; // error! implicit any
stillAProblem("").toFixed(); // okay
这似乎是 TypeScript 的设计限制或错误。
microsoft/TypeScript#6357 back in 2016 just says "unions of functions don't give you contextual typing" as if it is a fact of life, which it probably was back then. Since then, microsoft/TypeScript#37580 ran into this and calls it a bug. And microsoft/TypeScript#41213 也 运行 进入这个并称它为错误。所以也许这是一个已知的错误。但我不知道什么时候或是否会得到解决。
如果这是您唯一的问题,您可以做的权宜之计是显式注释 e2
回调参数:
const asyncHandler: Handler<number> = async (e2: object) => { return 1 }
但这当然不能解决上面的原始问题:
asyncHandler({}) // still an error
所以我这里的建议还是把asyncHandler
本身注解成AsyncHandler<number>
就完事了