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