Home > Enterprise >  Conditional return type based on input parameter
Conditional return type based on input parameter

Time:02-04

Description

I want to return either string or string[] based on the input parameter. In this case, the input parameter is called options and it is the input property: options.input.

I'm trying to make TypeScript infer it based on this input but I cannot seem to get it to work.

Types

The types are defined like this.

const TextInputTypes = <const>[
    "text",
    "number",
    "password",
    "email",
    "tel",
    "url",
    "search",
    "color",
    "date",
    "time",
    "datetime-local",
    "month",
    "week",
    "range",
    "file",
];
const CheckboxTypes = <const>["checkbox", "radio"];
const SelectTypes = <const>["select"];

type TextType = (typeof TextInputTypes)[number];
type CheckType = (typeof CheckboxTypes)[number] | (typeof SelectTypes)[number];
type InputType = TextType | CheckType;

type PromptOptions = {
    input: InputType;
    placeholder: string;
    entries: { value: string; text: string }[];
};

Example

If options.input is in TextType, then it returns string, else it returns string[].

Code

I have tried this without making it work

type ReturnType<T extends InputType> = T extends TextType ? string : string[];

const value: ReturnType<typeof options.input>;

It is basically within a function like so:

function foo = (name: string, options: Options) => {}

The output only says string | string[].

CodePudding user response:

Use function overloaing.

const TextInputTypes = [
    "text",
    "number",
    "password",
    "email",
    "tel",
    "url",
    "search",
    "color",
    "date",
    "time",
    "datetime-local",
    "month",
    "week",
    "range",
    "file",
] as const;
const CheckboxTypes = ["checkbox", "radio"];
const SelectTypes = ["select"] as const;
type TextType = (typeof TextInputTypes)[number];
type CheckType = (typeof CheckboxTypes)[number] | (typeof SelectTypes)[number];
type InputType = TextType | CheckType;

type PromptOptions<T extends InputType> = {
    input: T;
    placeholder: string;
    entries: { value: string; text: string }[];
};

const isCheckboxType = (typeString: string): typeString is CheckType => (CheckboxTypes as string[]).includes(typeString)

function foo (name: string, options: PromptOptions<TextType>): string
function foo (name: string, options: PromptOptions<CheckType>): string[]
function foo (name: string, options: PromptOptions<TextType | CheckType>): string | string[]  {
  if (CheckboxTypes.includes(options.input as CheckType)) {
    // TODO
    return ''
  }

  // transformToDocument
  return []
}

const arrayExample = foo('test', {input: 'select', placeholder: 'test', entries: []})
const stringExample = foo('test', {input: 'text', placeholder: 'test', entries: []})

enter image description hereUse function overloading.enter image description here

CodePudding user response:

Alternatively to the answers from @JDB and @adsy, you can use extends to achieve your goal:

type StringOrArrayString<T extends PromptOptions> = T extends {input: TextType} ? string : string[];

function foo<T extends PromptOptions>(name: string, options: T): StringOrArrayString<T> {
  // ...
}

Check this playground

CodePudding user response:

You can use function overloads to detect the type of the input property and then return either a string or a string[]. Note that this requires changing from an anonymous function (const foo = () => {}) to a named function (function foo() {}).

function isTextType(inputType:string):inputType is typeof TextInputTypes[number] {
    return (TextInputTypes as readonly string[]).includes(inputType);
}

type PromptOptions = {
    placeholder: string;
    entries: { value: string; text: string }[];
}
type TextTypePromptOptions = PromptOptions & {
    input: TextType;
};
type CheckTypePromptOptions = PromptOptions & {
    input: CheckType;
}


function foo(name:string, options:TextTypePromptOptions):string;
function foo(name:string, options:CheckTypePromptOptions):string[];
function foo(name:string, options:TextTypePromptOptions|CheckTypePromptOptions):string|string[] {
    if (isTextType(options.input)) {
        return "";
    } else {
        return [];
    }
}

// const result1: string
const result1 = foo("", { placeholder: "", entries: [], input: "text" });
// const result2: string[]
const result2 = foo("", { placeholder: "", entries: [], input: "radio" });

This is easy to read and understand for simple cases like yours, but if you have many such arguments then it could get a little tedious to write out all the possible overloads.

  • Related