简体   繁体   English

在 typescript 中,如何定义通用 function 的专用版本?

[英]in typescript, how to define a specialized version of a generic function?

if I have a generic function:如果我有一个通用的 function:

const f1 = <T>(x: T) => console.log(x)

I can define a specialized version for f1:我可以为 f1 定义一个专门的版本:

const f2 = (x: number) => f1(x)

then typescript will throw an error if I do this:然后 typescript 如果我这样做会抛出一个错误:

f2('6')

but f2 is actually another function that calls f1.但 f2 实际上是另一个调用 f1 的 function。

Is there a way to define f2 as a specialized version of f1?有没有办法将 f2 定义为 f1 的专用版本? something like:就像是:

const f2 = f1<number> // this doesn't work

You can define the generic type before like that您可以像这样定义泛型类型

type Fn<T = any> = (x: T) => void

const f1: Fn = (x) => console.log(x)
const f2: Fn<number> = f1
f2('6') // Argument of type '"6"' is not assignable to parameter of type 'number'.

Here is a link to a working playground 是一个工作游乐场的链接

I'm going to change your example function because the type <T>(x: T)=>void is not doing anything useful with the generic type parameter, and is practically the same as (x: unknown)=>void , a concrete function type.我将更改您的示例 function 因为类型<T>(x: T)=>void对泛型类型参数没有任何用处,实际上与(x: unknown)=>void相同,a具体 function 型。 Instead, let's look at a function of type <T>(x: T)=>T , which preserves the input type and outputs a value of the same type, where (x: unknown)=>unknown would not suffice:相反,让我们看一下<T>(x: T)=>T类型的 function ,它保留输入类型并输出相同类型的值,其中(x: unknown)=>unknown不够:

const f1 = <T>(x: T) => (console.log(x), x);
// const f1: <T>(x: T) => T

const n: number = 6;
const sixNumber = f1(n); // const sixNumber: number

const s: string = "6";    
const sixString = f1(s); // const sixString: string

What you want to do is take the generic function f1 and use it as if it were a concrete function f2 of type (x: number)=>number .您要做的是采用通用 function f1并将其用作(x: number)=>number类型的具体 function f2 This is a widening of the the type of f1 , since the every function of type <T>(x: T)=>T is also a function of type (x: number)=>number but not vice versa.这是f1类型的扩展,因为<T>(x: T)=>T类型的每个 function 也是(x: number)=>number类型的 function ,反之亦然。 The compiler recognizes this as a valid widening and you can just annotate f2 as the widened type yourself:编译器将此识别为有效的扩展,您可以自己将f2注释为扩展类型:

const f2: (x: number) => number = f1; // no error
const sixNumberOkay = f2(n); // okay
const sixStringNotOkay = f2(s); // error! string is not number

So that's good.所以这很好。

-- --

Now, as you noted, what you can't do is take the type of f1 and the type number and automatically produce the type of f2 .现在,正如您所指出的,您不能做的是采用f1的类型和类型number并自动生成f2的类型。 The type system has no way for you to represent substituting types for type parameters.类型系统无法让您表示类型参数的替换类型。 Neither f1<number> nor typeof f1<number> are valid. f1<number>typeof f1<number>均无效。 TypeScript doesn't have enough support for higher kinded types to express this, at least not purely at the type level . TypeScript 对更高种类的类型没有足够的支持来表达这一点,至少不是纯粹在类型级别

In TypeScript 3.4, TypeScript introduced improved support for inferring generic functions from other generic functions .在 TypeScript 3.4 中,TypeScript 引入了对从其他泛型函数推断泛型函数的改进支持 So while you can't do the type manipulation you're asking about at the type level alone, you can write some functions that make the compiler do that manipulation anyway.因此,虽然您不能仅在类型级别上进行您所询问的类型操作,但您可以编写一些函数让编译器无论如何都进行该操作。 For example:例如:

function specify<A extends any[], R>(f: (...a: A) => R) {
    return () => f;
}

The specify() function takes any function, even a generic one, and returns a new zero-arg function which is also generic, if the original one was. specify() function 接受任何 function,即使是通用的,并返回一个新的零参数 function,如果原始的也是通用的。 And that function will return the original function when called.并且调用时 function 将返回原始 function。 That allows you to do this:这使您可以这样做:

const f3 = specify(f1)<number>(); // const f3: (x: number) => number

specify(f1) returns a function of type <T>() => (x: T) => T . specify(f1)返回类型为<T>() => (x: T) => T的 function 。 And thus specify(f1)<number>() produces the non-generic (x: number)=>number function.因此specify(f1)<number>()产生非通用(x: number)=>number function。 And it should work at runtime too:它也应该在运行时工作:

const sixNumberStillOkay = f3(n); // okay
const sixStringStillNotOkay = f3(s); // error! string is not number

I'm not sure if you're going to be widening generic functions to concrete ones often enough for specify() to be worth it;我不确定您是否会经常将通用函数扩展到具体函数,以使specify()值得; that's up to you.这取决于你。


Okay, hope that helps;好的,希望有帮助; good luck!祝你好运!

Link to code 链接到代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM