'Why is the return type of `myOverloadedFunction(foo as any)` different from `ReturnType<typeof myOverloadedFunction>`?
I have a simple overloaded function like this:
function myFunction(input: string): string
function myFunction(input: undefined): undefined
function myFunction(input: string | undefined): string | undefined
function myFunction(input: string | undefined): string | undefined {
return input
}
This function works as expected with properly typed input:
const result1 = myFunction('foo') // Inferred type of result1: string
const result2 = myFunction(undefined) // Inferred type of result2: undefined
const input3: string | undefined = 'foo'
const result3 = myFunction(input3) // Inferred type of result3: string | undefined
However, when input is typed as any, the compiler assumes that the first overload is being used, even though an any can be undefined:
const result4 = myFunction(undefined as any) // Inferred type of result4: string
This is surprising to me (and incorrect!). I would expect result4 to be inferred as ReturnType<typeof myFunction> (which is string | undefined) instead.
What's going on here? What's the motivation for this behavior?
Solution 1:[1]
I stumbled upon this trick from the react-query codebase, and adapted it into an any-safe solution to this question:
type NonOptional<T> = Exclude<T, undefined | null>
// Needs a better name than SafeMap, I'm open to suggestions
type SafeMap<TIn, TOut> = 0 extends 1 & TIn
? TOut | undefined
: TIn extends NonOptional<TIn>
? NonOptional<TOut>
: TIn extends undefined
? undefined
: TOut | undefined;
function parseDate<T extends string | undefined>(arg: T): SafeMap<T, Date> {
// Fake implementation to satisfy the compiler for the sake of this example
return undefined as unknown as SafeMap<T, Date>
}
const anyArg: any = undefined
const stringArg: string = ''
const undefinedArg: undefined = undefined
const stringOrUndefinedArg: string | undefined = ''
const anyResult = parseDate(anyArg) // inferred type: `Date | undefined`
const stringResult = parseDate(stringArg) // inferred type: `Date`
const undefinedResult = parseDate(undefinedArg) // inferred type: `undefined`
const stringOrUndefinedResult = parseDate(stringOrUndefinedArg) // inferred type: `Date | undefined`
const _ = parseDate(42) // compilation error because 42 is not assignable to `string | undefined`
This works because any violates the normal rules of types--you might expect 1 & any to be 1, but it's actually any. So 0 extends 1 & any, but not 1 & AnyTypeOtherThanAny.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Matthew |
