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

pos*_*ita 15 generics 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
Run Code Online (Sandbox Code Playgroud)

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

import { useState } from "react"

type UseStateFnT<T,> = typeof useState<T>
const wrapUseState: (toWrap: UseStateFnT<T,>) => UseStateFnT<T,> = …
Run Code Online (Sandbox Code Playgroud)

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

jca*_*alz 19

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!
Run Code Online (Sandbox Code Playgroud)

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


另一方面,泛型函数具有特定类型,可以充当其类型参数的任何可能替换。泛型函数类型的值在使用时仍然是泛型的。类型参数附加到调用签名:

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

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

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


这两种泛型相互关联,但 TypeScript 类型系统的表达能力不足以谈论它们之间的关系。在其他一些语言中,您可能能够根据另一种语言来定义一个,例如

type GenFunc = forall T, GenType<T>; // not TS, error
Run Code Online (Sandbox Code Playgroud)

或者

type GenType<T> = instantiate GenFunc with T; // not TS, error
Run Code Online (Sandbox Code Playgroud)

但在 TypeScript 中你不能。也许如果我们按照microsoft/TypeScript#1213 的要求获得更高级的类型……但不是现在。所以不能直接在类型系统中以编程方式转成。GenFuncGenType


有一些邪恶的可怕方法可以强制编译器GenType根据GenFunc. 我所知道的方法是使用泛型类属性初始化和一些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[]
Run Code Online (Sandbox Code Playgroud)

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

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


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

Playground 链接到代码

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