Search code examples
typescriptgenericsdefault-valuereturn-typetyping

How does one type this default value in TypeScript?


Sorry for the vague title, but I don't know how to express this problem concisely.

Basically, I have the following code (with unimportant bits removed):

function identity<T>(value: T): T {
    return value;
}

function extent<T, K>(
    values: T[],
    map: (value: T) => K = identity // the '= identity' part gives an error
): [K, K] | undefined {
    let min;
    let max;

    // ... stuff ...

    return min === undefined || max === undefined ? undefined : [map(min), map(max)];
}

Where the default = identity value results in a compiler error.

I can remove the default value for the map parameter from the extent's signature and always provide one myself:

extent([5, 1, 3, 4, 8], x => x)

Which will work just fine, but I'd rather not supply the second parameter, unless I'm actually mapping the values from one type to another.

How do I type the indentity function so that it's accepted as the default value for extent's map parameter? I'm stuck on TypeScript 3.6 for the time being, if that matteres.


Solution

  • Probably the best way to deal with this that maintains type-safety to the outside world is via overloads:

    function extent<T>(values: T[]): [T, T] | undefined
    function extent<T, K>(values: T[], map: (value: T) => K): [K, K] | undefined
    function extent<T, K>(
      values: T[],
      map?: (value: T) => K 
    ): [K, K] | [T, T] | undefined {
      const actualMap = map ?? identity;
      let min;
      let max;
    
      // ... stuff ...
    
      return min === undefined || 
             max === undefined ? 
                 undefined : 
                 [actualMap(min), actualMap(max)];
    }