I'm trying to make a function that takes string
and returns number
include undefined
in the return type only if the argument included it.
Here's what I have (which I thought would work):
export function test<T extends string | undefined>(a: T)
:Exclude<boolean | T, string> {
if (a === undefined)
return undefined;
return true;
}
I hoped that Exclude<boolean | T, string>
would remove the string
from the string | undefined
leaving either undefined
or nothing (depending on the supplied argument type) however this code does not type-check, it says:
Type
'undefined'
is not assignable to type'boolean | Exclude<T, string>'
.
Unresolved conditional types (like Exclude<boolean | T, string>
with T
generic) are not often assignable. The compiler doesn't really know how to determine if something is of such a type because it does not try to iterate through all possible instantiations of the generic T
to see if the assignment is safe. So in these cases you usually assert that a value is that type, or you use function overloads so that the function implementation uses plain-old union types instead of conditional types. Here's the assertion solution:
export function test<T extends string | undefined>(a: T): Exclude<boolean | T, string> {
if (a === undefined)
return undefined as Exclude<boolean | T, string>; // asserted
return true;
}
And here's the overload solution:
export function test<T extends string | undefined>(a: T): Exclude<boolean | T, string>;
// overloaded
export function test(a: string | undefined): boolean | undefined {
if (a === undefined)
return undefined;
return true;
}
As long as they work the way you want when you call them with concretely-typed values, then conditional types should behave how you expect:
const definitelyDefined = test("hey"); // boolean, okay
const maybeUndefined = test(Math.random()<0.5 ? "hey" : undefined); // boolean | undefined, okay
(As an aside, I'd probably render your return type as boolean | (undefined extends T ? undefined : never)
:
export function test<T extends string | undefined>(
a: T
): boolean | (undefined extends T ? undefined : never) {
if (a === undefined)
return undefined as (undefined extends T ? undefined : never); // asserted
return true;
}
But that's just a matter of preference.)
Okay, hope that helps; good luck!
You could use function overloads to achieve this. Start by making your function accept either type and return either type, then add overloaded definitions to restrict which parameter & return types can be used together.
function test(a: string): boolean;
function test(a: undefined): undefined;
function test(a: string | undefined): boolean | undefined {
if (a === undefined)
return undefined;
return true;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With