TypeScript 函数在不期望的情况下通过了类型检查
TypeScript function passes type check when not expected to
我有一个函数 identityOrCall
,它要么调用给它的函数,要么 returns 值。 value
是泛型。
function identityOrCall <T>(value: T): T {
if (typeof value === 'function')
return value()
else
return value
}
identityOrCall(() => 'x') // -> 'x'
行 identityOrCall(() => 'x')
似乎通过了编译器的类型检查。
为什么?不应该报错吗?
如果 identityOrCall
被传递给一个函数,我希望泛型类型 T
被设置为 Function
并且 identityOrCall
应该 return a Function
类型。
相反,我可以将 Function
类型作为参数传递,并让 identityOrCall
return 成为 string
类型。
这是为什么?这对我来说似乎不一致,但我一定遗漏了一些东西。
问题在于,由于函数 return 类型在任何地方都没有注明,TypeScript 将其类型设为 any
。 any
不是 type-safe;它可以分配给任何东西。这里,TS 从 any
return 值中提取 T
(因为 any
可以缩小到 T)。
最好尽可能避免 any
类型。我喜欢使用 TSLint 的 no-unsafe-any
来禁止这种事情。
出于类似的原因,以下不会引发 TypeScript 错误(并且禁止使用上述 linting 规则),即使它显然不安全:
declare const fn: () => any;
function identityOrCall<T>(value: T): T {
return fn();
}
const result = identityOrCall(() => 'x') // -> 'x'
// result is typed as: () => 'x'. Huh?
如果目标是让方法根据参数表现不同,我认为最简单的方法是指定 overloads
function identityOrCall<T>(value: () => T): T;
function identityOrCall<T>(value: T): T;
function identityOrCall <T>(value: () => T|T): T {
if (typeof value === 'function')
return value()
else
return value
}
const result = identityOrCall(() => 'x') // first overload
const result2 = identityOrCall('x') // second overload
result.split(' '); // No error
result2.split(' '); // No error
result(); // error not callable
当调用方法时,TS 将通过重载来解析签名,直到找到一个匹配的(意味着定义重载的顺序在这里很重要)。
我有一个函数 identityOrCall
,它要么调用给它的函数,要么 returns 值。 value
是泛型。
function identityOrCall <T>(value: T): T {
if (typeof value === 'function')
return value()
else
return value
}
identityOrCall(() => 'x') // -> 'x'
行 identityOrCall(() => 'x')
似乎通过了编译器的类型检查。
为什么?不应该报错吗?
如果 identityOrCall
被传递给一个函数,我希望泛型类型 T
被设置为 Function
并且 identityOrCall
应该 return a Function
类型。
相反,我可以将 Function
类型作为参数传递,并让 identityOrCall
return 成为 string
类型。
这是为什么?这对我来说似乎不一致,但我一定遗漏了一些东西。
问题在于,由于函数 return 类型在任何地方都没有注明,TypeScript 将其类型设为 any
。 any
不是 type-safe;它可以分配给任何东西。这里,TS 从 any
return 值中提取 T
(因为 any
可以缩小到 T)。
最好尽可能避免 any
类型。我喜欢使用 TSLint 的 no-unsafe-any
来禁止这种事情。
出于类似的原因,以下不会引发 TypeScript 错误(并且禁止使用上述 linting 规则),即使它显然不安全:
declare const fn: () => any;
function identityOrCall<T>(value: T): T {
return fn();
}
const result = identityOrCall(() => 'x') // -> 'x'
// result is typed as: () => 'x'. Huh?
如果目标是让方法根据参数表现不同,我认为最简单的方法是指定 overloads
function identityOrCall<T>(value: () => T): T;
function identityOrCall<T>(value: T): T;
function identityOrCall <T>(value: () => T|T): T {
if (typeof value === 'function')
return value()
else
return value
}
const result = identityOrCall(() => 'x') // first overload
const result2 = identityOrCall('x') // second overload
result.split(' '); // No error
result2.split(' '); // No error
result(); // error not callable
当调用方法时,TS 将通过重载来解析签名,直到找到一个匹配的(意味着定义重载的顺序在这里很重要)。