【问题标题】:TypeScript function passes type check when not expected toTypeScript 函数在不期望的情况下通过了类型检查
【发布时间】:2021-02-21 22:59:54
【问题描述】:

我有一个函数identityOrCall,它要么调用给它的函数,要么返回值。 value 是泛型类型。

function identityOrCall <T>(value: T): T {
  if (typeof value === 'function')
    return value()
  else
    return value
}

identityOrCall(() => 'x') // -> 'x'

identityOrCall(() =&gt; 'x') 行似乎通过了编译器的类型检查。

为什么?不应该报错吗?

如果 identityOrCall 被传递一个函数,我希望泛型类型 T 设置为 Function 并且 identityOrCall 应该返回一个 Function 类型。

相反,我可以将Function 类型作为参数传递,并让identityOrCall 返回string 类型。

这是为什么?这对我来说似乎不一致,但我一定遗漏了一些东西。

【问题讨论】:

  • identityOrCall(() =&gt; 'x') 不会返回错误(为什么会这样?)在这种情况下,T 将是 () =&gt; string,这是一个有效类型
  • @apokryfos 我的误解是我希望identityOrCall 返回与其参数相同的类型。如果给定 Function 类型作为参数,我希望它返回 Function 类型。

标签: typescript types typescript-typings typing


【解决方案1】:

问题在于,由于函数返回类型没有在任何地方注明,TypeScript 将其类型设为anyany 不是类型安全的;它可以分配给任何东西。在这里,TS 从any 返回值中提取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?

【讨论】:

  • 我不确定这是否是正确的解释。我在我的tsconfig.json 中使用"noImplicitAny"。这似乎也没有给出编译错误:identityOrCall(function (): string { return 'x' })
  • 其实我想我现在明白了。所以T 在这里假设“最松散”的类型是any,而不是立即假设第一个T 类型化参数的类型。
【解决方案2】:

如果目标是让方法根据参数表现不同,我认为最简单的方法是指定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 将通过重载解析签名,直到找到匹配的签名(这意味着在这里定义重载的顺序很重要)。

Playground link

【讨论】:

    猜你喜欢
    • 2018-10-20
    • 2021-05-14
    • 1970-01-01
    • 2022-01-19
    • 2020-10-26
    • 1970-01-01
    • 2020-02-27
    • 2019-04-14
    • 2020-01-11
    相关资源
    最近更新 更多