如何使旧样式 类 在打字稿中工作?

How can I make old style classes work in typescript?

在将大量文件转换为 typescript 时,我有很多 classes 以这种方式声明。

function FooClass() {
    this.bar = 1; // error TS2683: 'this' implicitly has type 'any'
                  // because it does not have a type annotation.
}

FooClass.prototype.myMethod = function() {
    // ...
}

如何在启用严格类型检查的情况下完成这项工作,同时避免使用 class 语法重写它?

如果您不想转换它们,我建议将它们保留为 JavaScript 文件,只为它们编写声明文件。然后 TypeScript 会隐式识别它们,而无需重写代码。

您可以将 types.d.ts 文件添加到您的项目中,并且:

declare class FooClass {
    public bar: number;
    public myMethod: () => void;
    ...
}

在您的项目中使用这样的文件,TypeScript 将允许您执行 const myClass = new FooClass();

当然,您仍然需要添加 JavaScript 代码(否则您会 运行 进入 运行 时间错误),以适合您的方式导入它。如果您使用全局文件,通过将它们加载到您的页面或您正在使用的任何环境中。如果您的旧代码在模块中,则在需要使用它们时导入这些模块。

使上述代码工作的最简单方法是向函数添加一个 this parameter,如下所示:

function FooClass(this: {bar: number}) {
    this.bar = 1; // okay
}

不幸的是,您很快就会发现,当您将 FooClass 视为构造函数时,编译器不知道如何处理它:

const oops = new FooClass(); // error, oops is implicitly any
oops.bar // okay but compiler has no idea
oops.myMethod() // okay but also no idea
oops.foo // also okay, but you probably don't want it to be
oops.yourMethod() // ditto

这显然是by design。在我看来,最好的注释方法是预先定义类型 FooClassFooConstructor

interface FooClass {
  bar: number;
  myMethod(): void;
}

interface FooConstructor {
  new(): FooClass,
  prototype: FooClass
}

请注意,当您使用 class FooClass {} 创建构造函数的方式时,TypeScript 会自动生成一个 value FooClass(构造函数本身)和一个type FooClass 这是构造函数创建的实例的类型。这通常会让开发人员感到困惑,所以要小心。我们在这里手动执行此操作:上面的 interface FooClasstype,而不是我们将要创建的 value

定义这些类型后,在创建 FooClass 函数时断言其类型为 FooConstructor(断言需要通过 Functionany而且不安全,所以要小心)。

const FooClass = function FooClass(this: FooClass) {
  this.bar = 1;
} as Function as FooConstructor;

FooClass.prototype.myMethod = function () {
  // ...
}

并进行测试:

const okay = new FooClass();
okay.bar // number
okay.myMethod() // known
okay.foo // error, yay
okay.yourMethod() // error, yay

希望对您有所帮助;祝你好运!

我注意到 TypeScript v3.6.2 在 .js 文件的上下文中似乎表现不同。以下 ECMAScript 5 样式 class 似乎幸运地被类型推断并且检测到两个预期的类型违规:

/**
 * @class Person
 * @param {string} name
 */
function Person(name) {
  this.name = name
}

var person = new Person("John")
person.foo // TS2339: Property 'foo' does not exist on type 'Person'.
person.name

new Person(42) // Argument of type '42' is not assignable to parameter of type 'string'.

这可以通过以下方式确认:

$ tsc --checkJs --allowJs --noImplicitAny --strictNullChecks test.js

因此,恢复为 .js 文件可能是继续使用原始继承样式的一种方法。

然而,对于 subclasses,此方法似乎无效。我已将详细信息发布到 https://github.com/microsoft/TypeScript/issues/18171