我正在try 实现允许用户向已定义函数的类型添加一些参数的类型. 例如:

function sourceFunction(a: number, b: string): number {
    return a;
};
const extendedFunction: ExtendFunction<typeof sourceFunction, [boolean?]> = (a, b, flag) => {
    //where a: number, b: string, flag: boolean | undefined
    return a;
}

我已经实现了一些几乎可以完成这项工作的东西:

export type ExtendFunction<TFunction extends (...args: any) => any, TParameters extends any[]> = (
  ...args: [...Parameters<TFunction>, ...TParameters]
) => ReturnType<TFunction>;

但是,类型无法正确解析泛型函数,例如:

function sourceFunction<T>(a: T, b: string): T {
    return a;
};

const extendedFunction: ExtendFunction<typeof sourceFunction, [boolean?]> = (a, b, flag) => {
    //a is undefined instead of T
    return a;
}

有没有办法让ExtendFunction类型识别出传递的函数是泛型函数,并且在上面的例子中,返回值应该是T类型?

推荐答案

在这样的类型级别上,TypeScrip不能真正表达对generic个函数类型的操作.这种语言缺乏这样做所必需的那种更高级的亲切类型.因此,当T是泛型时,像Parameters<T>ReturnType<T>这样的conditional types将擦除类型参数.有不同的特性请求请求支持改进:"标准的"高级类型请求是microsoft/TypeScript#1213,尽管还不清楚这是否包括这个用例.无论如何,它不是语言的一部分.

目前,您在TypeScrip中能得到的最接近的结果是从类型级别下降到值级别,并使用对higher order type inference from generic functions的支持.也就是说,您不能将泛型函数类型F传递给泛型类型Transform<>,然后将转换后的泛型函数类型作为Transform<F>传出.但是您can获取一个F类型的泛型函数value f,将其传递给另一个泛型function transform(),并得到一个转换后的泛型函数value,其类型与您想要的Transform<F>相同.

也就是说,我们可以将ExtendedFunction<F, X>下拉到类似extendFunction<A, X, R>的地方(其中AR分别是F的参数和返回类型):

function extendFunction<A extends any[], X extends any[], R>(
    f: (...args: A) => R, x: X): (...args: [...A, ...X]) => R {
    return f as any
}

因此该函数接受类型(...args: A) => R(可能是泛型)函数f和类型X的元组,并返回类型(...args: [...A, ...X]) => R的新函数(可能是泛型).这就给了你这个:

function sourceFunction<T>(a: T, b: string): T {
    return a;
};

let extendedFunction = extendFunction(sourceFunction, null! as [boolean?]);
// let extendedFunction: <T>(a: T, b: string, args_2?: boolean | undefined) => T

extendedFunction = (a, b, flag) => {
    return a;
}

您将看到类型与您想要的完全一致.此外,extendFunction的实际实现并不重要(我只是使用any来 suppress 错误),因为您的目标只是操纵类型.

这种从类型层到值层的下降适用于一些更高类型的函数类型的用例,而不适用于其他类型的用例.如果您只想转换特定的函数类型,那么它可以很好地工作.但是,一旦您try 对这样的操作进行抽象(例如,将泛型方法的对象映射到相关泛型方法的另一个对象),您就会发现它根本不起作用.但是,除非出现真正的类型级支持,否则没有完美的方法.

Playground link to code

Typescript相关问答推荐

无法将select选项的值设置为ngFor循环中的第一个元素

如何根据模板类型实现不同的模板函数行为?

如何为父类构造函数中的修饰属性赋值?

如何在TypeScrip中从字符串联合类型中省略%1值

在不更改类型签名的情况下在数组并集上映射文字

为什么有条件渲染会导致material 自动完成中出现奇怪的动画?

使用Dockerfile运行Vite React应用程序时访问env变量

记录键的泛型类型

如何在使用条件类型时使用void

复选框Angular 在Ngfor中的其他块中重复

在类型脚本中将泛型字符串数组转换为字符串字母并集

为什么S struct 类型化(即鸭子类型化)需要非严格类型联合?

API文件夹内的工作员getAuth()帮助器返回:{UserID:空}

ANGLE NumberValueAccessor表单控件值更改订阅两次

在打字脚本中对可迭代对象进行可变压缩

使用或属性编写无限嵌套的接口

在抽象类属性定义中扩展泛型类型

如何为特定参数定义子路由?

如何在Type脚本中创建一个只接受两个对象之间具有相同值类型的键的接口?

带有 Select 器和映射器的Typescript 泛型