Typescript 生成带有 `#private;` 字段的声明 d.ts 文件

cin*_*mon 7 javascript typescript typescript-typings

我有一个用 Typescript 编写的库,它分布在 2 个文件中:一个编译的 ECMAScript-2015 兼容 Javascript 文件index.js和一个 Typescript 声明文件index.d.ts。我的目标是让 Javascript 和 Typescript 开发人员都可以访问库(以便他们具有正确的类型和自动完成功能)。

最近我升级到 Typescript 3.9.7,并决定重构我的代码以使用新的私有类字段声明,该声明利用#sigil 而不是 Typescript 的private关键字。

令我惊讶的是,index.d.ts由于#private;在我的课程中包含该成员,我的文件与旧的 Typescript 版本不兼容。

这是生成旧声明文件的旧 Typescript 代码与生成新的不兼容声明文件的新重构 Typescript 代码之间的比较。使用private关键字的旧代码:

// index.ts
class MyClass {
    private field1: string = "foo";
    private field2: string = "bar";

    constructor() {
        console.log(this.field1, this.field2);
    }
}

// generated index.d.ts
declare class MyClass {
    private field1;
    private field2;
    constructor();
}
Run Code Online (Sandbox Code Playgroud)

使用#sigil 声明私有名称的新重构代码:

// index.ts
class MyClass {
    #field1: string = "foo";
    #field2: string = "bar";

    constructor() {
        console.log(this.#field1, this.#field2);
    }
}

// generated index.d.ts
declare class MyClass {
    #private;
    constructor();
}
Run Code Online (Sandbox Code Playgroud)

这是Typescript 游乐场的一个页面,其中包含该示例代码。

现在,如果我的客户使用旧的 Typescript(比如 3.7 版)将获取我的库(由编译index.js和声明文件组成index.d.ts,没有源index.ts文件)并依赖index.d.ts类型,他们将看到以下错误:

error TS1127: Invalid character.
Run Code Online (Sandbox Code Playgroud)

该错误的起源很清楚(#印记),所以我的问题如下:

  1. 如果我在将我的库发送给客户之前对我的库进行后处理index.d.ts并删除该#private;行是否可以,而不必了解实施细节?我可以通过使用ttscpackage轻松做到这一点,但我仍然担心输入信息可能在某种程度上很重要。
  2. #private;line in的实际用途是index.d.ts什么?为什么声明文件会公开一个类使用私有字段,如果它们无论如何都无法访问,并且是实现细节?
  3. 根据Typescript Github 问题中的一个主题,这是预期的行为,以便具有私有字段的类在发送到.d.ts文件时保留其名义输入行为。可悲的是,那个解释的意义从我身上溜走了。是否有任何额外的文档我可以阅读以更好地理解 Typescript 的标称打字行为?

小智 6

它使类型成为“名义”类型,因此公开相同公共成员的其他类型不会被视为与具有私有字段的类型兼容。这很重要的一种情况是,如果您有这样的代码:

class C {
    #foo = "hello";
    bar = 123;

    static log(instance: C) {
        console.log("foo = ", instance.#foo, " bar = ", instance.bar);
    }
}
Run Code Online (Sandbox Code Playgroud)

我确信还有更多的例子,但这个静态方法只是我想到的一个。

C.log函数需要类的实际实例,C因为它访问instance参数上的私有命名实例字段。如果C通过表明它具有 ES 私有字段而没有反映该类型是名义类型,而只发出公共字段,则编译器将在此处使用结构类型比较,并且不会产生预期的类型错误。例如,该声明 emit 将允许依赖代码传入{ bar: 456 }C.log不会出现任何编译器错误。