Home > OS >  Typescript: return a value depends on generic type
Typescript: return a value depends on generic type

Time:08-29

I need to make this logic work.

const defaultNumberOption = {
  label: 'Select',
  value: 0,
};

const defaultStringOption = {
  label: 'Select',
  value: '',
};

const getDefaultOption = <ValueType extends string | number>(): IOption => {
  // if ValueType is 'string' - return defaultStringOption 
  // if ValueType is 'number' - return defaultNumberOption 
}

I tried to use different approaches, but with each of them had the same problem: I can't find a way to make a structure with types in condition, but with values in expression.

If there is a way to do it - it would be great.

CodePudding user response:

enum FixedLabel {Select}

const defaultNumberOption = {
 label:  FixedLabel.Select,
 value: 0,
};

const defaultStringOption = {
   label:  FixedLabel.Select,
   value: '',
};
type DefaultNumber = { label: FixedLabel, value: number};
type DefaultString = { label: FixedLabel, value: string};
type IOption = DefaultNumber | DefaultString;

const getDefaultOption = (v : number | string): IOption => {
 // if ValueType is 'string' - return defaultStringOption 
 // if ValueType is 'number' - return defaultNumberOption 
 if(typeof v === 'string'){
     return defaultNumberOption;
  } else {
   return defaultStringOption;
 }  
}

You can try this might work for you can also use string instead of enum if you are not sepcific to labels.

CodePudding user response:

You could use conditional types to achieve this.

interface DefaultNumberOption {
    label: 'Select',
    value: number,
}

interface DefaultStringOption {
    label: 'Select',
    value: string,
}

type DefaultOption<T extends number | string> = T extends string
    ? DefaultStringOption
    : DefaultNumberOption

const getDefaultOption = <T extends number | string>(value: T): DefaultOption<T> => {
    if (typeof value === 'string') {
        return {
            label: 'Select',
            value: ''
        } as DefaultOption<T>;
    }
    return {
        label: 'Select',
        value: 0
    } as DefaultOption<T>;
}

let x = getDefaultOption(''); // x will have type DefaultStringOption
let y = getDefaultOption(0); // y will have type DefaultNumberOption

Here is a link to a TS playground if you want to play around with the solution.

CodePudding user response:

TypeScript loses the typing information provided in <T extends string | number> when building to JS, which is why it does not allow you to use that.

In order to obtain the type, you may want to provide it as a parameter in the function.

const getDefaultOption = <T extends string | number>(optionValue: T): IOption => {
    return typeof optionValue === "string" ? defaultStringOption : defaultNumberOption;
}

It is also possible to add it to the return type instead:

const getDefaultOption = <T extends string | number>(getOptionType: T): IOption<string | number> => {
    if (typeof getOptionType === "string") return defaultStringOption;
    else return defaultNumberOption;
}

Note that IOption cannot contain <T> as TS would require the return type to be string | number instead of string or number.

Here's my TS playground

  • Related