Why the infered type of this function is `string | undefined`?

Why the infered type of this function is string | undefined? How TypeScript is unable to see that the returned value can never be undefined?
export const myFunction = (
{ value1 }: { value1?: string },
value2?: string
) => {
if (!value1 && !value2) {
throw new Error("No value provided.");
}

return value1 ?? value2;
};
export const myFunction = (
{ value1 }: { value1?: string },
value2?: string
) => {
if (!value1 && !value2) {
throw new Error("No value provided.");
}

return value1 ?? value2;
};
18 Replies
Benjamin
Benjamin•10mo ago
Even a simple version returns string | undefined:
export const myFunction = (value1?: string, value2?: string) => {
if (value1 == undefined && value2 == undefined) {
throw new Error("No value provided.");
}

return value1 != undefined ? value1 : value2;
};
export const myFunction = (value1?: string, value2?: string) => {
if (value1 == undefined && value2 == undefined) {
throw new Error("No value provided.");
}

return value1 != undefined ? value1 : value2;
};
Anna | DevMiner
Anna | DevMiner•10mo ago
this should be
if (value1 == undefined || value2 == undefined) {
throw new Error("No value provided.");
}
if (value1 == undefined || value2 == undefined) {
throw new Error("No value provided.");
}
then it'd be fine
Benjamin
Benjamin•10mo ago
Nope, you're changing the logic, I only want to throw if neither are defined (= both undefined). That's why TS should know at least one of them is defined and therefore the result is never undefined.
Anna | DevMiner
Anna | DevMiner•10mo ago
hmmm
Benjamin
Benjamin•10mo ago
I have to add as string and I hate that 😢
dan
dan•10mo ago
Its one of those cases where you cant get it to be inferred. The way it infers it technically is correct but I think as string is the only way.
Anna | DevMiner
Anna | DevMiner•10mo ago
how about
export const myFunction = (value1?: string, value2?: string) => {
if (value1 !== undefined) return value1;
if (value2 !== undefined) return value2;

throw new Error("No value provided.");
};
export const myFunction = (value1?: string, value2?: string) => {
if (value1 !== undefined) return value1;
if (value2 !== undefined) return value2;

throw new Error("No value provided.");
};
Benjamin
Benjamin•10mo ago
It was not exactly my use case, I am calling another function that takes a string as parameter. It works but it means I have to repeat the function call twice. Not ideal but maybe better than as string, I don't know. I guess that's the 2 solutions. ¯\_(ツ)_/¯
Anna | DevMiner
Anna | DevMiner•10mo ago
or you just use this as a separate function and then do something like this
export const actualFunction = (value1?: string, value2: string) => doSomeWork(myFunction(value1, value2));
export const actualFunction = (value1?: string, value2: string) => doSomeWork(myFunction(value1, value2));
Benjamin
Benjamin•10mo ago
yeah...
Kasper
Kasper•10mo ago
export const myFunction = ({ value1 }: { value1?: string }, value2?: string) => {
if (value1 && !value2) {
return value1
}

if (!value1 && value2) {
return value2
}

if (value1 && value2) {
return value2
}

throw new Error("No value provided.");
};
export const myFunction = ({ value1 }: { value1?: string }, value2?: string) => {
if (value1 && !value2) {
return value1
}

if (!value1 && value2) {
return value2
}

if (value1 && value2) {
return value2
}

throw new Error("No value provided.");
};
Not the best, but yeah
Benjamin
Benjamin•10mo ago
Yeah I think the as string is the most readable 😅
Kasper
Kasper•10mo ago
Yup
Benjamin
Benjamin•10mo ago
My post purpose was mainly to understand why TypeScript doesn't understand
dan
dan•10mo ago
Because to the type system after the first if check, both are typed as string | undefined. and it cant pickup on the fact that if value1 is undefined, value2 has to be defined.
Kasper
Kasper•10mo ago
Yeah, because it cant know which of them is not undefined.
Benjamin
Benjamin•10mo ago
but why if I only have one condition (value !== undefined), it manages, but not 2?
Anna | DevMiner
Anna | DevMiner•10mo ago
because then it's logical what changed