泛型函数的类型

TypeScript 2.8中的新 ReturnType是一个非常有用的特性,它允许您提取特定函数的返回类型。

function foo(e: number): number {
return e;
}


type fooReturn = ReturnType<typeof foo>; // number

然而,我在泛型函数的上下文中使用它遇到了麻烦。

function foo<T>(e: T): T {
return e;
}


type fooReturn = ReturnType<typeof foo>; // type fooReturn = {}


type fooReturn = ReturnType<typeof foo<number>>; // syntax error


type fooReturn = ReturnType<(typeof foo)<number>>; // syntax error

有没有一种方法可以提取泛型函数给出特定类型参数的返回类型?

37477 次浏览

TypeScript compiler does not see typeof foo as generic type. I'd say it's a bug in the compiler.

However, TypeScript has 可调用的接口 which can be generic without any problems, so if you introduce a callable interface compatible with the signature of your function, you can implement your own equivalent of ReturnType like this:

function foo<T>(x: T): T {
return x;
}




interface Callable<R> {
(...args: any[]): R;
}


type GenericReturnType<R, X> = X extends Callable<R> ? R : never;


type N = GenericReturnType<number, typeof foo>; // number

如果你想得到一些特殊的泛型类型,你可以使用一个假的函数来包装它。

const wrapperFoo = () => foo<number>()
type Return = ReturnType<typeof wrapperFoo>

更复杂的演示

function getList<T>(): {
list: T[],
add: (v: T) => void,
remove: (v: T) => void,
// ...blahblah
}
const wrapperGetList = () => getList<number>()
type List = ReturnType<typeof wrapperGetList>
// List = {list: number[], add: (v: number) => void, remove: (v: number) => void, ...blahblah}

如果你能改变函数的定义,我找到了一个很好的简单的方法来实现这个目标。 在我的例子中,我需要使用带有通用函数的类型 Parameters,准确地说,我是在尝试使用 Parameters<typeof foo<T>>,但它实际上不能工作。因此,实现这一点的最佳方法是改变 函数定义接口函数定义,这也将与类型 ReturnType的打字机工作。

下面是 OP 所描述的案例的一个例子:

function foo<T>(e: T): T {
return e;
}


type fooReturn = ReturnType<typeof foo<number>>; // Damn! it throws error


// BUT if you try defining your function as an interface like this:


interface foo<T>{
(e: T): T
}


type fooReturn = ReturnType<foo<number>> //it's number, It works!!!
type fooParams = Parameters<foo<string>> //it also works!! it is [string]


//and you can use the interface in this way
const myfoo: foo<number> = (asd: number) => {
return asd;
};


myfoo(7);

这是我目前正在使用的解决方案,用于提取未导出的导入库的内部类型(比如 knex) :

// foo is an imported function that I have no control over
function foo<T>(e: T): InternalType<T> {
return e;
}


class Wrapper<T> {
// wrapped has no explicit return type so we can infer it
wrapped(e: T) {
return foo<T>(e)
}
}


type FooInternalType<T> = ReturnType<Wrapper<T>['wrapped']>
type Y = FooInternalType<number>
// Y === InternalType<number>
const wrapperFoo = (process.env.NODE_ENV === 'typescript_helper' ? foo<number>(1) : undefined)!
type Return = typeof wrapperFoo

运动场

下面是另一个用例,其中存在一些未导出的默认类型。

// Not exported
interface Unaccessible1 {
z: number
x: string
}


function foo1<T extends Unaccessible1>(e: T): T {
return e;
}


const wrapperFoo1 = (process.env.NODE_ENV === 'typescript_helper' ? foo1.apply(0, 0 as any) : undefined)!
type ReturnFoo1 = typeof wrapperFoo1 // Unaccessible1


interface Unaccessible2 {
y: number
c: string
}


function foo2<T extends Unaccessible2>(e: T, arg2: number, arg3: string, arg4: Function): T {
return e;
}


const wrapperFoo2 = (process.env.NODE_ENV === 'typescript_helper' ? foo2.apply(0, 0 as any) : undefined)!
type ReturnFoo2 = typeof wrapperFoo2 // Unaccessible2

运动场

我找到了一个解决方案。你决定它是否适合你的需要:)

使用接口声明函数参数和返回类型

interface Foo<T, V> {
(t: T, v: V): [T, V]
}

使用 ParametersReturnType以这种方式实现您的功能

function foo<T, V>(...[t, v]: Parameters<Foo<T, V>>): ReturnType<Foo<T, V>> {
return [t, v]; // [T, V]
}

正常调用函数,或者使用 ReturnType获取返回类型

foo(1, 'a') // [number, string]
type Test = ReturnType<Foo<number, number>> // [number, number]

这在以前是不可能做到的,在一个纯粹的通用方式,但将在类型4.7。这种模式称为“实例化表达式”。相关的公关是 给你。节选自描述:

function makeBox<T>(value: T) {
return { value };
};


const makeStringBox = makeBox<string>;  // (value: string) => { value: string }
const stringBox = makeStringBox('abc');  // { value: string }


const ErrorMap = Map<string, Error>;  // new () => Map<string, Error>
const errorMap = new ErrorMap();  // Map<string, Error> ```

...

一个特别有用的模式是 为引用 type parameters in type instantiation expressions:

type BoxFunc<T> = typeof makeBox<T>;  // (value: T) => { value: T }
type Box<T> = ReturnType<typeof makeBox<T>>;  // { value: T }
type StringBox = Box<string>;  // { value: string }