As you've defined it, a function of type MergeFunction must work for any type T that the caller specifies. So arrayMerge is not a MergeFunction, since it only accepts arrays. Here's one way to implement your MergeFunction as specified:
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;
In fact, the only thing you can safely do when implementing a type like MergeFunction is to return one of the inputs, because you don't know anything about T since the caller is in charge of that. There's certainly no way to be sure that T is an array.
Perhaps you mean for MergeFunction to be a type where the implementer chooses the generic parameter T. In this case, you can make the type generic instead of the function:
declare type MergeFunction<T> = (def: T, file?: T, arg?: T) => T;
Note how the <T> moved from the function to the type. The original definition is a specific type alias which refers to a generic function type, while the new definition is a generic type alias which, when you plug in a value for T, refers to a specific function type. (Sorry if that's confusing.) It is now much easier to implement some specific type of this. For example:
const concatenateStrings: MergeFunction<string> =
(def: string, file?: string, arg?: string) =>
def + (file ? file : "") + (arg ? arg: "");
The function concatenateStrings is a MergeFunction<string>.
At this point it seems like it should be simple to represent arrayMerge as some kind of MergeFunction<>. Unfortunately it isn't. TypeScript lacks the sort of generics you need here. What you want to say is something like:
const arrayMerge: <T> MergeFunction<T[]> = // invalid syntax
(def: T[], file: T[] = [], arg: T[] = []): T[] =>
([] as T[]).concat(def).concat(file || []).concat(arg || []);
But you can't do that directly (as the linked issue describes). The closest you can get is to add a layer of indirection, such as a function call:
const makeArrayMerge = <T>(): MergeFunction<T[]> =>
(def: T[], file: T[] = [], arg: T[] = []): T[] =>
([] as T[]).concat(def).concat(file || []).concat(arg || []);
Now makeArrayMerge is a function that, when called with a specified type parameter T, produces a MergeFunction<T>. This works, but is harder to use (and doesn't infer types the way you'd like):
const numArray = makeArrayMerge<number>()([0, 1, 2], [3, 4, 5]);
Oh well, that's the best I can do given the limitations of TypeScript generics. It's up to you to decide if you really need the above indirection or if some specific array type will work for you. Hope that helps. Good luck!
MergeFunction?MergeFunctions. One takes three strings and returns a string, one takes three numbers and returns a number, etc. However, when passed around, it's generalized as aMergeFunction, not its implementation. The goal here was to be able to pass this general type around, and it would still work the same regardless of how it acted underneath. That is,merge(1,2,3)returns anumberwhilemerge('foo','bar','baz')returns astring, etc.