Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Typescript dynamic union return type based on arguments

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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?

>Solution :

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading