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

Posted

技术标签:

【中文标题】TypeScript 如何为泛型函数创建泛型类型别名?【英文标题】:TypeScript how to create a generic type alias for a generic function? 【发布时间】:2020-07-03 18:32:52 【问题描述】:

给定一个类型化的泛型函数,我想为该函数创建一个泛型别名,但似乎我做不到。换句话说,这是行不通的:

// 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,> = …

如果不自己重新创建the complex typed function signature,这可能吗?

【问题讨论】:

【参考方案1】:

TypeScript 中有两种不同风格的泛型:泛型 functions 和泛型 types...看起来您希望编译器将一种转换为另一种给你,不直接支持。


要明确:

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

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 是一个泛型类型。需要指定类型参数才能将其用作值的类型,然后生成的类型就不再是泛型了。 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 函数在被调用时仍然是泛型的。

泛型函数(包括泛型构造函数)可以被认为是generic values,而不是泛型类型。


这两种泛型是相互关联的,但 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 中引入的一些 higher order type inference for generic functions。当我实际上没有任何值时,我让编译器认为它正在计算值,然后获取这些假装值之一的类型:

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&lt;T&gt;GenType&lt;T&gt; 的类型相同,如果您将GenFunc 更改为具有一个类型参数的任何泛型函数,GenType2&lt;T&gt; 将相应更改。

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


无论如何,希望对您有所帮助;祝你好运!

Playground link to code

【讨论】:

我想我必须读三遍才能开始理解它,但这反映了我对 Typescript 细微差别的无知,而不是你的回答,它是彻底且写得好的。感谢那!我接受这一点是希望未来的一些评论或回应将是“这现在可以从 TypeScript . ... ”,这在 Stack 上的许多 TypeScript 问题中似乎很常见溢出。

以上是关于TypeScript 如何为泛型函数创建泛型类型别名?的主要内容,如果未能解决你的问题,请参考以下文章

在 TypeScript 中推断泛型函数类型

讲给初学者的typescript泛型

TypeScript 泛型

TypeScript-泛型

TypeScript 中每个键的泛型映射对象类型

TypeScript 泛型函数的类型