使用TypeScript将箭头函数分配给通用函数类型

时间:2018-06-19 17:17:15

标签: typescript generics

我已经在类似问题上做了一些探索,但是找不到可行的解决方案。我有一些通用函数,但是我似乎无法正确实现它们。

简而言之,我有这个:

/** Takes three values of the same type and collapses them into one */
declare type MergeFunction = <T>(def: T, file?: T, arg?: T) => T

/** The implementation I'm having trouble with. Merge three arrays of the same type. */
const arrayMerge: MergeFunction = <T>(def: T[], file: T[] = [], arg: T[] = []): T[] => [ ].concat(def).concat(file || [ ]).concat(arg || [ ])

但是,出现编译错误:

Property 'arrayMerge' is incompatible with index signature.
  Type '<A>(def: A[], file?: A[], arg?: A[]) => A[]' is not assignable to type 'MergeFunction'.
    Types of parameters 'def' and 'def' are incompatible.
      Type 'T' is not assignable to type '{}[]'.

我实际上如何实现这种类型?

1 个答案:

答案 0 :(得分:5)

如您所定义,类型MergeFunction的函数必须对调用者指定的任何类型T起作用。因此arrayMerge不是 一个MergeFunction,因为它只接受数组。这是按照指定方式实现MergeFunction的一种方法:

declare type MergeFunction = <T>(def: T, file?: T, arg?: T) => T;
const returnLastSpecifiedThing: MergeFunction = <T>(def: T, file?: T, arg?: T) =>
  typeof arg !== 'undefined' ? arg : 
  typeof file !== 'undefined' ? file : 
  def;

实际上,实现MergeFunction之类的类型时,您唯一可以做的安全的事情就是返回其中一个输入,因为you don't know anything about T是由调用者负责的。当然,无法确定T是一个数组。


也许您是说MergeFunction是一种类型,其中实现器选择通用参数T。在这种情况下,您可以使 type 通用,而不是 function

declare type MergeFunction<T> = (def: T, file?: T, arg?: T) => T;

请注意<T>是如何从函数移动到类型的。原始定义是特定类型别名,它引用泛型函数类型,而新定义是泛型类型别名,当您插入T的值,指的是特定功能类型。 (抱歉,这很令人困惑。)现在,实现 some 特定于此的类型要容易得多。例如:

const concatenateStrings: MergeFunction<string> = 
  (def: string, file?: string, arg?: string) =>
    def + (file ? file : "") + (arg ? arg: "");

函数concatenateStringsMergeFunction<string>

在这一点上,将arrayMerge表示为某种MergeFunction<>似乎很简单。不幸的是,事实并非如此。 TypeScript缺少此处需要的the sort of generics。您想要说的是这样的:

const arrayMerge: <T> MergeFunction<T[]> = // invalid syntax
  (def: T[], file: T[] = [], arg: T[] = []): T[] =>
    ([] as T[]).concat(def).concat(file || []).concat(arg || []);

但是您不能直接这样做(如链接的问题所述)。您可以获得的最接近的方法是添加一个间接层,例如函数调用:

const makeArrayMerge = <T>(): MergeFunction<T[]> =>
  (def: T[], file: T[] = [], arg: T[] = []): T[] =>
    ([] as T[]).concat(def).concat(file || []).concat(arg || []);

现在makeArrayMerge是一个函数,当使用指定的类型参数T进行调用时,产生一个MergeFunction<T>。这可以工作,但是更难使用(并且不会以您想要的方式推断类型):

const numArray = makeArrayMerge<number>()([0, 1, 2], [3, 4, 5]);

哦,鉴于TypeScript泛型的局限性,这是我能做的最好的事情。由您决定是否确实需要上述间接寻址或某些特定的数组类型是否适合您。希望能有所帮助。祝你好运!