TypeScript 添加类型运算符,可以回答"给定函数类型T,当参数P传入时,其返回值的类型是什么?"

uinbv5nw  于 2个月前  发布在  TypeScript
关注(0)|答案(8)|浏览(33)

搜索词

重载推断函数运算符参数返回值

建议

// Already possible
type ValueForKey<T, K extends keyof T> = T[K];

// Proposal
type Callable = (...args: any[]) => any;
type ValueForArguments<T extends Callable, A extends paramsof T> = T(...A);

用例

这是一个建议,它将有助于解决重载函数问题,如 #26591 ,并将帮助我的 type testing library 能够支持检查,如 expectTypeOf(fn).toBeCallableWith(args)
请注意,尽管 ValueForKey 可以实现为:

type ValueForKey<T, K extends keyof T> = 
  T extends {[Key in K]: infer U} ? U : never;

以下仅适用于非重载函数:

type ValueForArguments<T extends Callable, P extends Parameters<T>> = 
  T extends (...args: P) => infer R ? R : never;

因为从 Parameters<T> 的推断和类型定义都将只考虑其中一个重载。
我所要求的是让 paramsofT(A1, A2, ...A) 类型语法能够正确地与重载函数一起使用,而不是让推断考虑多个重载。

示例

interface OverloadedFunction {
  (arg: number): number;
  (arg: string): string;
}

// `number`
type Result1 = OverloadedFunction(number);

// `number | string`
type Result2 = OverloadedFunction(number | string);

// `[number] | [string]`
type Result3 = paramsof OverloadedFunction;

// `number | string`
type Result4 = OverloadedFunction(...paramsof OverloadedFunction)

另外

鉴于上述情况,也有一些像这样的东西是有意义的:

type Newable = new (...args: any[]) => any;
type ValueForArguments<T extends Newable, A extends paramsof new T> = new T(...A);

其中 new TT 的构造函数类型转换为函数类型

检查清单

我的建议满足以下准则:

  • 这不会对现有的 TypeScript/JavaScript 代码造成破坏性更改
  • 这不会改变现有 JavaScript 代码的运行时行为
  • 这可以在不根据表达式的类型发出不同的 JS 的情况下实现
  • 这不是一个运行时特性(例如库功能、JavaScript 输出的非 ECMAScript 语法等)
  • 这个特性将与 TypeScript's Design Goals 的其他部分保持一致。
e4eetjau

e4eetjau1#

请注意,尽管ValueForKey可以实现为:

type ValueForKey<T, K extends keyof T> = 
   T extends {[Key in K]: infer U} ? U : never;

但我不得不收回这个声明,因为我发现了一个目前不起作用的例子。

type Test = {
    x: boolean;
    [K: string]: number | boolean;
}

let boolean: ValueForKey<Test, 'x'> = true;

// @ts-expect-error
boolean = 42

// Type 'boolean' is not assignable to type 'never'.(2322)
let shouldBeNumberOrBoolean: ValueForKey<Test, 'y'> = true
    ^^^^^^^^^^^^^^^^^^^^^^^

// Type 'number' is not assignable to type 'never'.(2322)
shouldBeNumberOrBoolean = 42;
^^^^^^^^^^^^^^^^^^^^^^^

let numberOrBoolean: Test['y'] = true;

numberOrBoolean = 42;

这意味着T[...]语法提供了其他方式无法产生的唯一语义。

q5lcpyga

q5lcpyga2#

这是我使用当前状态得到的最接近的解决方案。
它在大多数情况下都能工作。
但是在带有可选参数的重载中存在几个问题。

xtupzzrd

xtupzzrd3#

这是另一个用例:
实际:

function usesOverloadedFunction<T>(arg: T) // : inferred return type is whatever the last overload of
// `overloadedFunction` is when applied to `...args: [T]`. 
{
  return overloadedFunction(arg);
}

期望:

function usesOverloadedFunction<T>(arg: T) // : infers `(typeof overloadedFunction)(T)`
{
  return overloadedFunction(arg);
}
cxfofazt

cxfofazt4#

希望看到解决这个问题的方法。这是我不喜欢使用标准NodeJS EventEmitter的原因之一。这些事件经常有10多个以上的重载,所有事件名都需要手动输入。当你试图对事件进行编码时,这很好,但目前任何优雅的类型推断都是不可能的。

具体的例子是events.onevents.once,它们是两个通过promise轻松与emitter交互的两级函数,例如:

event.once(emitter, name[, options]) // => return a Promise
event.on(emitter, name[, options]) // => return an AsyncIterable

当你使用大量的async/await时,这两个函数都很棒,它可以节省在等待某些init/close-event(如net.Serverlisteningclose)时的恼人的回调嵌套。这会导致一些非常漂亮的线性代码。

但是TypeScript无法为你提供任何关于任何emitter上存在的事件的完成帮助,这使得如果不熟悉emitter的话,这两个函数使用起来非常繁琐。

server.listen(8124)
await once(server, 'listening')
server.close()
await once(server, 'close')

解决这个问题也可能成为与泛型函数相关问题的一个垫脚石,更智能的ReturnTypebind(...args)操作。我希望能最终准确地使用柯里化和泛型。

ivqmmu1c

ivqmmu1c5#

这是一个关于重复问题的讨论。根据提供的信息,这个问题与之前的某个问题有关,该问题被关闭为重复问题,但最终被拒绝。对于当前的问题,无法确定其具体情况。

bgibtngc

bgibtngc7#

从用户的Angular 来看,这个功能非常有用。我希望官方能更多地从用户的Angular 出发。

szqfcxe2

szqfcxe28#

交叉链接到#52035(评论)以及具体术语“呼叫类型”

相关问题