类型 'Observable<false>' 不可分配给类型 'Observable<boolean>'

Type 'Observable<false>' is not assignable to type 'Observable<boolean>'

我们正在使用 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 是可行的,但我确实认为这是 hack,而不是解决方案(显然我们需要对每次出现的 true/false 进行转换)。 有什么办法可以真正解决这个问题?

编辑:根据评论,很明显某些类型检查已更改。可以在此处查看更多示例。 Playground Link.

是否有关于此更改的任何信息(以及解释)?

Edit2:按照评论中的建议,我在 https://github.com/microsoft/TypeScript/issues/48150

提交了错误报告

根据 user who has responded to the Github issue (https://github.com/microsoft/TypeScript/issues/48150) 预计 Typescript 4.6 编译错误:

I believe this is a correct error which was not handled properly in old versions. The generic parameter T is invariant as it is used in both a covariant position () => T and a contravariant position (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>

Playground Link

由于主要问题与 Knockout.js 库有关,因此我将问题提交到那里 (https://github.com/knockout/knockout/issues/2589),希望具有足够 Typescript 知识的人可以解决该问题。

我建议改为显式指定通用类型,例如:

const x = observable<boolean>(false);

原因是我们假设 false 意味着boolean。通常是这样,但并非总是如此。想象一下,例如这些类型

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

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