英文:
Type definition for property names of function returning void
问题
I'm currently writing a small RPC library for a project I'm working on. To ensure some level of correctness I'm using typescript to do some typechecking. I got the initial implementation working and while doing some refactoring I ran into a problem with some of the conditional types.
The idea is to have an RPC service definition along the lines of the interface below and be able to separate calls that return a value (call) to those that don't (notify).
export default interface ExampleService {
add(a: number, b: number): number;
sub(a: number, b: number): number;
log(message: string): void;
}
Up to now I was returning Promises and it was working fine, but switching the return type to literal/void the types are no longer correct
type CallMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => void ? never : K;
}[keyof T];
type NotifyMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => void ? K : never;
}[keyof T];
I'm expecting CallMethods<ExampleServer>
to be "add" | "sub" and NotifyMethods<ExampleService>
to be "log."
But with this type definition, I'm getting CallMethods<ExampleService>
as "never" and NotifyMethods<ExampleService>
as "add" | "sub" | "notify"...
My assumption is that it has to do with substitutability as explained in https://github.com/microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void
英文:
I'm currently writing a small RPC library for a project I'm working on. To ensure some level of correctness I'm using typescript to do some typechecking. I got the initial implementation working and while doing some refactoring I ran into a problem with some of the conditional types.
The idea is to have an RPC service definition along the lines of the interface below and be able to separate calls that return a value (call) to those that don't (notify).
export default interface ExampleService {
add(a: number, b: number): number;
sub(a: number, b: number): number;
log(message: string): void;
}
Up to now I was returning Promises and it was working fine, but switching the return type to literal/void the types are no longer correct
type CallMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => void ? never : K;
}[keyof T];
type NotifyMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => void ? K : never;
}[keyof T];
I'm expecting CallMethods<ExampleServer>
to be "add" | "sub"
and NotifyMethods<ExampleService>
to be "log"
But with this type definition I'm getting CallMethods<ExampleService>
as never
and NotifyMethods<ExampleService>
as "add" | "sub" | "notify"
...
My assumption is that it has to do with substituability as explained in https://github.com/microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void
答案1
得分: 0
可以这样写(参见 [playground][1])
```ts
export default interface ExampleService {
add(a: number, b: number): number;
sub(a: number, b: number): number;
log(message: string): void;
}
type CallMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => (infer R extends NonNullable<infer Z>) ? K : never;
}[keyof T];
type NotifyMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => (infer R extends NonNullable<infer Z>) ? never : K;
}[keyof T];
type CallMethodsList = CallMethods<ExampleService>
type NotifyMethodsList = NotifyMethods<ExampleService>
为了检测 void
返回类型,我添加了 NonNullable
检查。这种解决方案的缺点是 null
或 undefined
的返回类型也会被分类为 NotifyMethods
,但我目前找不到其他解决办法。
<details>
<summary>英文:</summary>
You can write it like this (see [playground][1])
```ts
export default interface ExampleService {
add(a: number, b: number): number;
sub(a: number, b: number): number;
log(message: string): void;
}
type CallMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => (infer R extends NonNullable<infer Z>) ? K : never;
}[keyof T];
type NotifyMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => (infer R extends NonNullable<infer Z>) ? never : K;
}[keyof T];
type CallMethodsList = CallMethods<ExampleService>;
type NotifyMethodsList = NotifyMethods<ExampleService>;
In order to detect void return type I've added NonNullable check. Downside of this solution is that return type of null
or undefined
would also classify as NotifyMethods but I couldn't find other way around it for now.
答案2
得分: 0
以下是代码部分的翻译:
正如您所指出的,的确是因为非 void 返回函数可以分配给 void 返回函数。要解决这个问题,只需检查返回类型 only。不要在这里比较整个函数类型。
比较类型是否为 void 使用 extends
不会产生这种行为。
这也适用于函数的返回类型可为可空类型(包括 null 或 undefined)。
英文:
As you noted, it is indeed because non-void returning functions are assignable to void returning functions. To get around this, simply check the return type only. Do not compare function types as a whole here.
type CallMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? ReturnType<T[K]> extends void ? never : K : never;
}[keyof T];
type NotifyMethods<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? ReturnType<T[K]> extends void ? K : never : never;
}[keyof T];
Comparing types to void with extends
does not have this behavior.
type Y = number extends void ? true : false;
// ^? false
This also works if the return type of a function is nullable (includes null or undefined).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论