1

I'm trying to create a converter interface with the following semantics:

  1. The input value may be undefined.
  2. If no default is passed then the result may be undefined.
  3. If a defined default is passed then the result is guaranteed not be undefined.

And have this enforced with type checking at compile time.

To add another layer, the underlying converter functions that I need to wrap sometimes take parameters, so I need some kind of wrapper to create them.

I can achieve exactly what I need with a concrete class:

// simple example - insert the separator between each digit of a number
const addSeperator = (num: number, sep: string) => String(num).replace(/(.)(?=.)/g, '$&' + sep);

class NumStringConverter {
  constructor(private sep: string) {}
  convert(val: number): string;
  convert(val: number | undefined): string | undefined;
  convert(val: number | undefined, dflt: string): string;
  convert(val: number | undefined, dflt?: string): string | undefined {
    return val !== undefined ? addSeperator(val, this.sep) : dflt;
  };
}

const dasherize = new NumStringConverter('-');

let result: string;
result = dasherize.convert(123);                // '1-2-3'
result = dasherize.convert(123, 'empty');       // '1-2-3'
result = dasherize.convert(undefined);          // desired error - does not compile
result = dasherize.convert(undefined, 'empty'); // 'empty'

But I can't find a way to extend this to an interface:

interface NumStringConverter {
  convert(val: number): string;
  convert(val: number | undefined): string | undefined;
  convert(val: number | undefined, dflt: string): string;
}

function createNumStringConverter(sep: string): NumStringConverter {
  return {
    // *** this fails with a long type error ***
    convert: (val: number | undefined, dflt?: string): string | undefined => {
      return val !== undefined ? addSeperator(val, sep) : dflt;
    }
  };
}

const dasherize = createNumStringConverter('-');

Is this possible?

I eventually need to parameterize this to be:

interface Converter<T, U> {
  convert(val: T):  U;
  convert(val: T | undefined):  U | undefined;
  convert(val: T | undefined, dflt:  U):  U;
}

But I think that should be easy if I can get the basic typed interface to work.

Other similar questions, but nothing there that I could find to solve this problem:

Barney
  • 2,786
  • 2
  • 32
  • 34
  • just set the return type of the implementation to `any` see this answer https://stackoverflow.com/a/34799303/1041641 – TmTron Feb 15 '21 at 12:09

0 Answers0