首页 > 解决方案 > Typescript:返回给定类型函数的类型

问题描述

我想创建一个返回其泛型类型参数的函数的类型。它的行为应该像keyof但只返回用于调用函数的键

我正在使用打字稿 3.4.5。

用例:

type FunctionOf<T> = ... // <-- what I want

function testFunction<T, U extends FunctionOf<T>>(obj: T, functionName: U): T[U] { 
  return obj[functionName]();
}

我的具体情况:

export type Mapper<T, U> = (value: T, index?: number, array?: T[]) => U;

type IsFunction<T> = T extends Function ? T & Function : never;

export function toField<T, U extends keyof T, V extends IsFunction<T[U]>>(key: U): Mapper<T, V extends () => infer R ? R : never> {
    return (value: T) => (value[key] as V)();
}

class Test {
    x: string;
    constructor(x: string) {
        this.x = x;
    }

    getX(): string { return this.x; }
}

const y: Test[] = [new Test('hello')];

y.map(toField('getX')); // putting in 'x' will not cause the entire return to be never which is what I want.

我希望以上内容将接受的参数限制为的功能,Test但它没有

编辑:我可能已经找到了解决方案,但只有当我使用“任何”时(我通常没有不安全的任何)。这正确地推断出返回类型应该是never当提供的字段不是函数时,但不会导致任何编译错误。虽然这解决了我的具体情况,但我仍然想找到一种更有效地输入它的方法

export type Mapper<T, U> = (value: T, index?: number, array?: T[]) => U;

type IsFunction<T> = T extends Function ? T & Function : never;

export function toField<T, U extends keyof T, V extends IsFunction<T[U]>>(
    key: U
): V extends Function ? Mapper<T, V extends () => infer R ? R : never> : never {
    return ((value: T) => (value[key] as V)()) as any;
}

标签: typescript

解决方案


以下是我的处理方式:

function toField<K extends keyof any>(key: K) {
  return <T extends Record<K, () => any>>(value: T): ReturnType<T[K]> => value[key]();
}

这里,toField()是一个泛型函数 in Kkey参数的类型,它返回一个泛型函数 in Tvalue参数的类型。 T被限制为具有 key 属性的类型K,其在该键处的属性值是一个零参数函数。 函数返回一个类型的值ReturnType<T[K]>,其中在标准库ReturnType<F>中定义为函数类型的返回类型的类型。 F

让我们测试一下:

class Test {
  x: string;
  constructor(x: string) {
    this.x = x;
  }

  getX(): string { return this.x; }
}

const y: Test[] = [new Test('hello')];
const strings = y.map(toField('getX')); // okay, string[]
const doesntWork = y.map(toField('x')); // error!
//                       ~~~~~~~~~~~~ 
// y.map() doesn't accept a toField('x'), since Test['x'] is not a zero arg fn

我认为最好y.map(toField('x'))是编译时警告,而不是在编译时默默地产生类型值never[]。你根本不想传递toField('x')y.map()

还有几个案例:

const thisWorks = toField('toLowerCase')("LOWERCASE");
const thisDoesnt = toField('charAt')("oops"); // error!
//                                   ~~~~~~
// "oops".charAt is a function but needs args 

你可以打电话"LOWERCASE".toLowerCase(),但不应该不能打电话"oops".charAt()

最后,请注意,您的原始类型实际上只在一行中起作用,arr.map(toField("xxx"))因为它依赖于上下文类型arr.map()推断toField(). 你这样做会遇到麻烦:

const getXField = toField('getX');
// const getXField: <T extends Record<"getX", () => any>>(value: T) => ReturnType<T["getX"]>
const stringsTwoStep = y.map(getXField); // okay, string[]

对于您的原始代码,第一行将是一个错误,因为它无法推断出任何内容T,默认为{}or unknown,然后最终限制Unever,并且'getX'不可分配给never。布莱。但是在上面的代码中,在一行中调用它或将它分成两行没有区别。

好的,希望有帮助。祝你好运!

链接到代码


推荐阅读