网站搜索

typescript - 类型 'Observable' 不能赋值给类型 'Observable'


我们正在使用 Knockout.js (v3.5.0) 及其 TypeScript 定义。它们在 TypeScript 4.6.2 之前工作正常。然而,问题似乎比定义文件中的情况“更深”。似乎 TypeScript 在处理布尔类型时有所变化。因此,我没有将这个问题标记为 Knockout.js 问题,而是创建了一个受 Knockout d.ts 启发的小示例代码来说明这个问题:

interface Observable<T>
{
  (): T;
  (value: T): any;
}

function observable<T>(value: T): Observable<T>
{
  return undefined as any;  // the implementation is not important
}

const x: Observable<boolean> = observable(false);

这段代码有一个编译问题:

Type 'Observable<false>' is not assignable to type 'Observable<boolean>'.
  Types of parameters 'value' and 'value' are incompatible.
    Type 'boolean' is not assignable to type 'false'.

当然,将 false 强制转换为 boolean 是可行的,但我认为这是一种变通方法,而不是解决方案(显然我们需要对每个 true/false 的出现都进行转换)。有没有办法真正解决这个问题?

编辑:根据评论,很明显某些类型检查已被更改。更多示例可以在这里看到。游乐场链接。

有关于此更改的任何信息(附解释)吗?

编辑2:按照评论中的建议,我在 https://github.com/microsoft/TypeScript/issues/48150 提交了一个错误报告

答案#1

我建议改为明确指定泛型类型,如:

const x = observable<boolean>(false);

原因是我们正在假设false意味着boolean。通常确实如此,但并非总是。比如想象这些类型

const x = observable<false | undefined>(false);
const y = observable(false);

(对于任何试图以其他方式解决打字问题的人,请确保上述内容也是有效的。)

答案#2

根据回复了 Github 问题(https://github.com/microsoft/TypeScript/issues/48150)的用户,Typescript 4.6 的编译错误是预期的:

我相信这是一个在旧版本中未被正确处理的正确错误。泛型参数 T 是不变的,因为它既用于协变位置 (() => T),也用于逆变位置 ((value: T) => any)。

这确实是真的。既然用户帮助解决了问题,为了完整起见,我将尝试在这里重新表述并总结他的评论。

第一个提出的解决方案只能部分解决问题:

function observable<T>(value: T extends infer U ? U: never): Observable<T>
{
  return undefined as any;  // the implementation is not important
}

const x: Observable<boolean> = observable(false); // no error

然而,如果没有类型注解,这将产生 unknown 类型。

const bad = observable(false);
//     ^ unknown, which should be Observable<boolean>

正确的解决方案似乎是引入另一个类型参数

declare function observable<T, U = any>(value?: T extends infer R ? R : U): 
    unknown extends T ? Observable<U> : Observable<T>

游乐场链接

由于主要问题出在 Knockout.js 库上,我在那儿提交了问题(https://github.com/knockout/knockout/issues/2589),希望有足够 TypeScript 知识的人能解决这个问题。