Typescript:返回给定类型函数的类型

时间:2019-06-05 18:42:30

标签: 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的函数,但不是这样

编辑:我可能已经找到了解决方案,但是只有当我使用“ any”(通常没有不安全的any)时,我才找到解决方案。当提供的字段不是函数但不会引起任何编译错误时,这正确地推断出返回类型应为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;
}

1 个答案:

答案 0 :(得分:0)

这是我的处理方法:

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

在这里,toField()K参数类型的key中的泛型函数,它返回 T中的泛型函数,即value参数的类型。 T限于类型具有键为K的属性,该属性在该键处的值为零参数函数。 那个函数返回类型为ReturnType<T[K]>的值,其中ReturnType<F>defined in the standard library是函数类型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()中的contextual typing来推断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推断任何内容,默认为{}unknown,然后最终约束{{ 1}}到U,并且never无法分配给'getX'。布莱但是在上面的代码中,在一行中调用或将其拆分为两行没有区别。

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

Link to code