Home > other >  Typescript dynamic union return type based on arguments
Typescript dynamic union return type based on arguments

Time:03-29

I have a function that looks like this:

function getFirstDefined(...values: any) {
    for (let value of values) {
        if (!!value) {
            return value;
        }
    }
    return undefined;
}

It basically gets the first non undefined value in the argument list and returns it.

The problem is I have to add types to it, and I would like for it to have a dynamic union type based on the types of the arguments passed to the method.

For example:

const var1: string = "Hello world!";
const var2: number;
const var3: boolean = false;

// Should have return type: string | number | boolean | undefined
getFirstDefined(var1, var2, var3);

I tried to do this:

function getFirstDefined<T extends any>(...values: T[]): T | undefined {
    for (let value of values) {
        if (!!value) {
            return value;
        }
    }
    return undefined;
}

But it "locks" the function and only allows values of the same type.

const var1: string = "Hello world!";
const var2: number;
const var3: boolean = false;

// Error: var2 and var3 are not strings
getFirstDefined(var1, var2);

Is it possible to do something like this?

CodePudding user response:

You can instead change your generic to be an array itself. Then, it will allow the items to be different. After that, index the array generic by number to get a union of the types of the values in the array and put that with a union to undefined which should get your expected behavior.

function getFirstDefined<T extends unknown[]>(...values: T): T[number] | undefined {
    for (let value of values) {
        if (!!value) {
            return value;
        }
    }
    return undefined;
}

const foo = getFirstDefined("hey", 2, 5); // string | number | undefined
const asdf = getFirstDefined(5, "hey"); // string | number | undefined
const asdf2 = getFirstDefined(); // undefined

TypeScript Playground Link

  • Related