使用类似命名空间结构扩展 class 时 TypeScript 的奇怪行为

Strange behaivour of TypeScript when extending class with similar namespace structure

最近我一直在尝试为用JS编写的库创建一个类型库。当我声明所有命名空间、classes 和接口时,一些 classes 开始给我一个错误 TS2417。我检查了是否存在无效覆盖方法或属性的问题,但我找不到任何东西。一段时间后,我发现有问题的 class 与命名空间之一同名(例如 class A.B 和命名空间 A.B)。但这并没有造成任何麻烦。问题是(有问题的 class 的父 class 和有问题的 class 一样,命名空间完全相同,这个命名空间和有问题的 class 的命名空间=27=] 有 class,名称完全相同,但界面不同(我很难描述这个问题,所以我 simulated here)。

所以问题是,是什么导致了这个问题?

在此示例中,有问题的 class 是 A.C,但由于 classes A.C.XA.B.X 的构造函数不兼容,它会出现问题。

declare namespace A {
  class B {

  }

  namespace B {
    class X {

    }
  }

  class C extends A.B {

  }

  namespace C {
    class X {
      constructor(x: number)
    }
  }
}

(下面我会省去命名空间A)

construct signature, the static side of a class is checked for substitutability the same way that the instance side is; so if you have class X { static prop: string = "" }, then you can't have class Y extends X { static prop: number = 2}. When you say class Y extends X you are declaring that, among other things, Y.prop is assignable to X.prop. See microsoft/TypeScript#4628 以外的更多信息。不是每个人都喜欢这种限制,但它确实存在。


这意味着以下内容应该可以正常工作:

const X: typeof B.X = C.X; // should be okay
new X(); // should be okay

但是您的 classes 的具体实现很容易导致运行时错误:

class B {
  static X = class { }
}

class C extends B {
  static X = class {
    constructor(x: number) {
      x.toFixed();
    }
  }
}

此处,C.X 是一个 class 构造函数,需要 number 输入。但是 B.X 是一个 class 构造函数,它根本不需要任何输入。如果您将前者视为后者,那么在运行时您将在 undefined 上调用 toFixed() 方法。哎呀

这就是 class C extends B 生成编译器错误的原因;保护您免受 class.

的静态方面引起的可替代性问题

Playground link to code