Typescript使用#private;字段生成声明d.ts文件 [英] Typescript generates declaration d.ts file with `#private;` field

查看:165
本文介绍了Typescript使用#private;字段生成声明d.ts文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用Typescript编写的库,该库分布在2个文件中:一个已编译的ECMAScript-2015兼容Javascript文件 index.js 和一个Typescript声明文件 index.d.ts .我的目标是使Java开发人员和Typescript开发人员都可以访问库(以便他们进行正确的键入和自动填充).

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

令我惊讶的是,由于我的类中包含 #private; 成员,因此我的 index.d.ts 文件与旧的Typescript版本不兼容.

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

 //index.ts类MyClass {专用字段1:字符串="foo";专用栏位2:string ="bar";Constructor(){console.log(this.field1,this.field2);}}//生成的index.d.ts声明类MyClass {私人领域1;私人领域2;构造函数();} 

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

 //index.ts类MyClass {#field1:字符串="foo";#field2:字符串=栏";Constructor(){console.log(this.#field1,this.#field2);}}//生成的index.d.ts声明类MyClass {#私人的;构造函数();} 

下面是在打字稿操场包含该示例代码.

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

 错误TS1127:无效字符. 

该错误的根源很明显(#标记),所以我的问题如下:

  1. 如果在将库运送给客户之前对我的 index.d.ts 进行后处理并删除 #private; 行,可以吗?知道实施细节吗?我可以使用 ttsc 包轻松地做到这一点,但我仍然担心键入信息的重要性.
  2. index.d.ts #private; 行的实际用途是什么?为什么声明文件为什么会公开类利用私有字段(如果无论如何也不能访问它们)以及实现细节?
  3. 根据 Typescript Github问题中的一个主题,这是预期的行为,因此带有私有字段的类在发送到 .d.ts 文件时仍保留其名义上的键入行为.可悲的是,那个解释的意思从我身边溜走了.我是否可以阅读其他文档以更好地了解Typescript的名义打字行为?

解决方案

它使类型"nominal"成为标称值.因此,公开相同公共成员的其他类型将不被视为与具有私有字段的类型兼容.一种重要的情况是您是否具有以下代码:

  C类{#foo ="hello";bar = 123;静态日志(实例:C){console.log("foo =",instance.#foo,"bar =",instance.bar);}} 

我敢肯定还有更多的例子,但是这种静态方法只是我脑海中浮现的一种方法.

C.log 函数需要 C 类的实际实例,因为它访问 instance 参数上的私有实例字段.如果发出的声明没有表明 C 类型是名义上的,则表明它具有ES私有字段,而仅发出公共字段,则编译器将在此处使用结构类型比较,并且不会产生预期的类型错误.例如,该声明发出将允许相关代码在没有任何编译器错误的情况下将 {bar:456} 传递给 C.log .

I have a library, written in Typescript, that is being distributed in 2 files: a compiled ECMAScript-2015 compatible Javascript file index.js and a Typescript declaration file index.d.ts. My goal is to make library accessible for both Javascript and Typescript developers (so that they have proper typings and autocomplete).

Lately I have upgraded to Typescript 3.9.7, and decided to refactor my code to use new private class fields declaration that utilizes # sigil instead of Typescript's private keyword.

To my surprise, my index.d.ts file become non-compatible with old Typescript versions due to including the #private; member on my classes.

Here is a comparison between old Typescript code generating old declaration file, and a new refactored Typescript code that generates a new non-compatible declaration file. The old code utilizing private keyword:

// 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();
}

The new refactored code that uses # sigil to declare private names:

// 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();
}

Here is a page at Typescript playground that contains that sample code.

Now, if my customer that uses an old Typescript (let's say, version 3.7) will fetch my library (consisting of compiled index.js and declaration file index.d.ts, without the source index.ts file) and rely on index.d.ts types, they'll see the following error:

error TS1127: Invalid character.

The origin of that error is clear (the # sigil), so my questions are following:

  1. Is it okay if I postprocess my index.d.ts and remove the #private; line before I ship my library to customers, that don't have to know about implementation details? I can easily do that by using ttsc package, but I still worry that piece of typing information might be somehow important.
  2. What is the practical use for #private; line in index.d.ts? Why would a declaration file expose that a class utilizes private fields, if they can't be accessed anyway, and are implementation details?
  3. According to a topic in Typescript Github issues, this is the intended behavior so that classes with private fields retain their nominal typing behavior when emitted to a .d.ts file. Sadly, the meaning of that explanation slips away from me. Is there any extra documentation I can read to better understand the nominal typing behavior of Typescript?

解决方案

It makes the type "nominal" so that other types which expose the same public members are not seen as compatible with a type that has a private field. One case where this matters is if you have code like this:

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

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

I'm sure there are more examples, but this static method is just one that came to my head.

This C.log function requires an actual instance of the C class since it accesses a private-named instance field on the instance parameter. If the declaration emit doesn't reflect that the C type is nominal by indicating that it has an ES private field and instead only emits the public fields, the compiler will use structural type comparisons here and won't produce the expected type errors. For example, that declaration emit would allow dependent code to pass in { bar: 456 } to C.log without any compiler error.

这篇关于Typescript使用#private;字段生成声明d.ts文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆