首页 > 解决方案 > 使用 TypeScript 将箭头函数分配给通用函数类型

问题描述

我已经对类似的问题进行了一些挖掘,但我找不到有效的解决方案。我有一些类型的通用函数,但我似乎无法正确实现它们。

简而言之,我有这个:

/** 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 '{}[]'.

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

标签: typescriptgenerics

解决方案


正如您所定义的,类型函数MergeFunction必须适用于调用指定的任何类型。所以不是a ,因为它只接受数组。这是按照指定实现您的一种方法:TarrayMergeMergeFunctionMergeFunction

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就是返回其中一个输入,因为您对此一无所知,T因为调用者负责该操作。当然没有办法确定这T是一个数组。


也许您的意思是MergeFunction成为实现者选择泛型参数的类型T。在这种情况下,您可以将类型设为泛型而不是函数

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

请注意如何<T>从函数移动到类型。原始定义是一个特定的类型别名,它指的是一个泛型函数类型,而新定义是一个泛型类型别名,当你插入一个值时T,它指的是一个特定的函数类型。(对不起,如果这令人困惑。)现在实现某些特定类型的 this 要容易得多。例如:

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

函数concatenateStrings是一个MergeFunction<string>

在这一点上,似乎应该很简单地表示arrayMerge为某种MergeFunction<>. 不幸的是,事实并非如此。TypeScript 缺少你需要的那种泛型。想说的是这样的:

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 || []);

NowmakeArrayMerge是一个函数,当使用指定的类型参数调用时T,它会生成一个MergeFunction<T>. 这有效,但更难使用(并且不会以您想要的方式推断类型):

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

哦,好吧,鉴于 TypeScript 泛型的限制,这是我能做的最好的事情。由您决定是否真的需要上述间接方式,或者某些特定的数组类型是否适合您。希望有帮助。祝你好运!


推荐阅读