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

Typescript generates declaration d.ts file with `#private;` field

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

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

令我惊讶的是,由于在我的 classes 中包含 #private; 成员,我的 index.d.ts 文件变得与旧的 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();
}

使用# 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();
}

这是包含该示例代码的 a page at Typescript playground

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

error TS1127: Invalid character.

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

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

#private 语法目前是 stage-3 proposal for javascript。一旦浏览器支持它,它就不会被打字稿转译。

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

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

    static log(instance: C) {
        console.log("foo = ", instance.#foo, " bar = ", instance.bar);
    }
}

我敢肯定还有更多示例,但这个静态方法只是我想到的一个。

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

我试图回答你的问题但无法回答,然后我出于好奇问了我自己的问题,这是由一位 TypeScript 贡献者回答的,你可以在这里找到他的答案:What's the purpose of #private in TypeScript definition files?

总而言之,在某些情况下,私有字段在比较类型时很重要,这就是 #private 字段出现的原因,因此“包含私有成员”的信息是类型的一部分定义。