TypeScript 如何为泛型函数创建泛型类型别名?



给定一个类型化的泛型函数,我想为该函数创建一个泛型别名,但似乎我不能。换句话说,这不起作用:

// foo.tsx
function foo<T>(arg: T): T {
return arg
}
type FooT = typeof foo  // works, but alias isn't generic: <T>(arg: T) => T
type FooParametersT = Parameters<typeof foo>  // sure: [unknown]
type FooReturnT = ReturnType<typeof foo>  // no problem: unknown
type GFooT<T,> = typeof foo<T,>  // yikes
type GFooParametersT<T,> = Parameters<typeof foo<T,>>  // nope
type GFooReturnT<T,> = ReturnType<typeof foo<T,>>  // fails

我真正想做的是在别人的库中获取函数的类型,然后围绕它构建一个接口。例如:

import { useState } from "react"
type UseStateFnT<T,> = typeof useState<T>
const wrapUseState: (toWrap: UseStateFnT<T,>) => UseStateFnT<T,> = …

如果不自己重新创建复杂的类型化函数签名,这是否可能?

TS4.7+ 的更新

再次问好! TypeScript 4.7 将引入在 microsoft/TypeScript#47607 中实现的实例化表达式,这将允许您直接为泛型函数指定类型参数,而无需实际调用该函数,因此不再需要像答案的其余部分所示那样滥用类。 在这里:

type GFooT<T,> = typeof foo<T>  // (arg: T) => T
type GFooParametersT<T,> = Parameters<typeof foo<T>>  // [arg: T]
type GFooReturnT<T,> = ReturnType<typeof foo<T>>  // T

这与问题中的语法几乎相同,只是在类型参数之后不支持尾随逗号(但在类型参数声明之后支持它们(。万岁!

操场链接到代码


TS4.6- 的先前答案

TypeScript 中有两种不同风格的泛型:泛型函数和泛型类型...看起来您希望编译器为您将一个转换为另一个,这不是直接支持的。


要清楚:

泛型类型具有需要指定的类型参数,然后才能将其用作特定类型。 例如:

type GenType<T> = (x: T) => T[];
declare const oops: GenType; // error
declare const genT: GenType<string>; // okay
const strArr = genT("hello"); // string[];
const numArr = genT(123); // error!

在这里,GenType是泛型类型。 您需要指定 type 参数以将其用作值的类型,然后生成的类型不再是泛型。genT函数接受string并返回string[]。 它不能用作接受number并返回number[]的函数。

另一方面,

泛型函数具有特定类型,其作用类似于其类型参数的任何可能替换。 泛型函数类型的值在使用时仍然是泛型函数。 类型参数附加到调用签名:

type GenFunc = <T>(x: T) => T[];
declare const genF: GenFunc;
const strArr = genF("hello"); // strArr: string[];
const numArr = genF(123); // numArr: number[];

在这里,GenFunc是引用泛型函数的特定类型。 调用genF函数时仍然是泛型的。

泛型函数(包括泛型构造函数(可以被视为泛型值,而不是泛型类型。


这两种风格的泛型是相互关联的,但 TypeScript 类型系统的表现力不足以讨论它们之间的关系。 在其他语言中,您也许可以根据另一种语言来定义一种语言,例如

type GenFunc = forall T, GenType<T>; // not TS, error

type GenType<T> = instantiate GenFunc with T; // not TS, error

但在 TypeScript 中你不能。 也许如果我们按照 microsoft/TypeScript#1213 中的要求获得更高种类的类型......但不是现在。 因此,在类型系统中以编程方式将GenFunc转换为GenType是不可能的。


有一些邪恶的可怕方法可以强迫编译器根据GenFunc计算GenType。 我所知道的方式是利用泛型类属性初始化和TypeScript 3.4中引入的泛型函数的一些高阶类型推断。 我让编译器认为它正在计算值,而实际上没有任何值,然后获取这些假装值之一的类型:

class GenTypeMaker<T> {
getGenType!: <A extends any[], R>(cb: (...a: A) => R) => () => (...a: A) => R;
genType = this.getGenType(null! as GenFunc)<T>()
}
type GenType2<T> = GenTypeMaker<T>['genType']
// type GenType2<T> = (x: T) => T[]

您可以验证GenType2<T>是否与GenType<T>的类型相同,如果将GenFunc更改为具有一个类型参数的任何泛型函数,GenType2<T>将相应地更改。

但我不知道我想推荐任何人实际使用这种方法。 而且它并没有真正缩放或组成;如果在一个类型参数中有一个充满泛型函数的对象,并且想要将其转换为一个充满具有指定类型参数的特定函数的对象,则如果不对每个对象属性执行一次操作,则无法使用此方法从类型系统获取它。

>游乐场链接到代码

最新更新