为什么这个多态赋值在 Knockout 中的类型化 observableArray 中不起作用?

Why this polymorphic assignment does not work in typed observableArray in Knockout?

让我们有 class A 和 subclass B。我想将 B 的数组分配给 A 的数组。对于普通数组,它按预期工作,但对于 ko.ObservableArray 它失败了。

import ko from "knockout";

class A {};
class B extends A {b = 1};

const a: A = new B();                // no problem here
const aArr: A[] = [] as B[];         // no problem here
// COMPILER PROBLEM ON LAST LINE!
const aObs: ko.ObservableArray<A> = ko.observableArray() as ko.ObservableArray<B>;

编译器对最后一行不满意,显示此错误:

Type 'ObservableArray<B>' is not assignable to type 'ObservableArray<A>'.
  Types of parameters 'value' and 'value' are incompatible.
    Type 'A[] | null | undefined' is not assignable to type 'B[] | null | undefined'.
      Type 'A[]' is not assignable to type 'B[]'.
        Type 'A' is not assignable to type 'B'.

但显然我不想将 A 分配给 B,而是 B 分配给 A

如何应对?

问题是 ObservableArray<T> is contravariant 它的类型变量 T。这就是它尝试反向分配类型的原因。

虽然您可以自由地将 B 分配给 A(因为 B extends A 并且您甚至不必将其声明为 extends)当它进入协变时位置:

class A { }
class B { b = 1 }

const a: A = new B();
const aArr: A[] = [] as B[];

playground link

您不能在同一方向上分配接受这些类型参数的函数。只在对面。

type Fn<T> = (t: T) => void

const fn1: Fn<A> = null as unknown as Fn<B> // error here
const fn2: Fn<B> = null as unknown as Fn<A> // no error

playground link


以防万一您绝对确定赋值完全正确并且编译器不必要地限制您。您可以自行承担责任并将其转换为预期类型:

import ko, { ObservableArray } from 'knockout'

class A {};
class B extends A {b = 1};

const bObs: ObservableArray<B> = ko.observableArray()
const aObs: ObservableArray<A> = bObs as unknown as ObservableArray<A>;

playground link


您还可以使用 --strictFunctionTypes 选项全局关闭 fn 参数的逆变检查。虽然大多数时候这是非常有用的检查。