为什么 return 类型的 `Overloaded Function(foo as any)` 不同于 `Return Type<typeof myOverloadedFunction>`?
Why is the return type of `myOverloadedFunction(foo as any)` different from `ReturnType<typeof myOverloadedFunction>`?
我有一个像这样的简单重载函数:
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
}
此函数在正确输入的情况下按预期工作:
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
但是,当 input
被键入为 any
时,编译器假定正在使用第一个重载,即使 any
可以是 undefined
:
const result4 = myFunction(undefined as any) // Inferred type of result4: string
这让我感到惊讶(而且不正确!)。我希望 result4
被推断为 ReturnType<typeof myFunction>
(即 string | undefined
)。
这是怎么回事?这种行为的动机是什么?
我从 react-query
代码库中偶然发现了 this trick,并将其改编为针对此问题的 any
安全解决方案:
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`
之所以可行,是因为 any
违反了类型的正常规则——您可能认为 1 & any
是 1
,但实际上是 any
。所以 0
扩展 1 & any
,但不扩展 1 & AnyTypeOtherThanAny
。
我有一个像这样的简单重载函数:
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
}
此函数在正确输入的情况下按预期工作:
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
但是,当 input
被键入为 any
时,编译器假定正在使用第一个重载,即使 any
可以是 undefined
:
const result4 = myFunction(undefined as any) // Inferred type of result4: string
这让我感到惊讶(而且不正确!)。我希望 result4
被推断为 ReturnType<typeof myFunction>
(即 string | undefined
)。
这是怎么回事?这种行为的动机是什么?
我从 react-query
代码库中偶然发现了 this trick,并将其改编为针对此问题的 any
安全解决方案:
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`
之所以可行,是因为 any
违反了类型的正常规则——您可能认为 1 & any
是 1
,但实际上是 any
。所以 0
扩展 1 & any
,但不扩展 1 & AnyTypeOtherThanAny
。