Sjiep
Sjiep

Reputation: 758

Split string and convert array entries to number or string with 'narrowed' type inference

So I have this function that splits a string based on the provided separator and will convert the values of the array to either a string or number based on the convertTo value.

If I call this function with convertTo = 'number', typescript still assumes the result is of type Array<string | number>, but ideally I would like it to be of type Array<number>.

What would be the way to achieve this in Typescript?

type Options = {
  separator?: string;
  convertTo?: 'string' | 'number';
};

export function splitAndTrim(value: string, options: Options = {}): Array<string | number> {
  const { separator = ',', convertTo = 'string' } = options;
  return value.split(separator).map(entry => {
    if (convertTo === 'string') {
      return entry.trim();
    }
    return Number(entry);
  });
}

// parsedValue is still of type Array<string | number>. 
// Is there a way of having it of type Array<number>? I was hoping TS could infer that.
const parsedValue = splitAndTrim(value, {convertTo: 'number'});

Note the function works as I expect, the question is regarding typescript typings.
Any help is appreciated!

Upvotes: 1

Views: 1645

Answers (1)

Your function can return either array of strings or array of numbers, so the return type of the function will always be Array. But you always can check the type of parsedValue with "typeof" operator

Edit: Try function overloading

You can have multiple functions with the same name but different parameter types and return type

Your code should look something like this:

export function splitAndTrim(value: string, returnNum: true, separator?: string): Array<number>;
export function splitAndTrim(value: string, returnNum: false, separator?: string): Array<string>;

export function splitAndTrim(value: string, returnNum: boolean, separator: string = ','): any {
    return value.split(separator).map(entry => {
        if (returnNum) {
            return Number(entry); 
        }
        return entry.trim();
    });

}

const a = splitAndTrim('1', true); // number[] type
const b = splitAndTrim('1', false); // string[] type

Upvotes: 2

Related Questions