7
votes

Typescript génère un fichier de déclaration d.ts avec le champ `#private;`

J'ai une bibliothèque, écrite en Typescript, qui est distribuée en 2 fichiers: un fichier Javascript compilé compatible ECMAScript-2015 index.js et un fichier de déclaration index.d.ts . Mon objectif est de rendre la bibliothèque accessible aux développeurs Javascript et Typescript (afin qu'ils aient des typages et une saisie semi-automatique appropriés).

Dernièrement, j'ai mis à niveau vers Typescript 3.9.7 et j'ai décidé de refactoriser mon code pour utiliser une nouvelle déclaration de champs de classe privée qui utilise # sigil au lieu du mot-clé private Typescript.

À ma grande surprise, mon fichier index.d.ts plus compatible avec les anciennes versions de #private; raison de l' #private; membre sur mes cours.

Voici une comparaison entre l'ancien code Typescript générant un ancien fichier de déclaration et un nouveau code Typescript refactorisé qui génère un nouveau fichier de déclaration non compatible. L'ancien code utilisant private mot-clé private :

error TS1127: Invalid character.

Le nouveau code refactoré qui utilise # sigil pour déclarer les noms privés:

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

Voici une page sur le terrain de jeu Typescript qui contient cet exemple de code.

Maintenant, si mon client qui utilise un ancien Typescript (disons, la version 3.7) va chercher ma bibliothèque (composée de index.js compilé et du fichier de déclaration index.d.ts , sans le fichier source index.ts ) et s'appuie sur index.d.ts types, ils verront l'erreur suivante:

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

L'origine de cette erreur est claire (le # sigil), donc mes questions sont les suivantes:

  1. index.d.ts je post- index.d.ts mon index.d.ts et supprimer le #private; ligne avant d'expédier ma bibliothèque aux clients, qui n'ont pas besoin de connaître les détails de mise en œuvre? Je peux facilement le faire en utilisant le package ttsc , mais je crains toujours que les informations de saisie ne soient importantes.
  2. Quelle est l'utilisation pratique de #private; ligne dans index.d.ts ? Pourquoi un fichier de déclaration exposerait-il qu'une classe utilise des champs privés, s'ils ne sont pas accessibles de toute façon, et sont des détails d'implémentation?
  3. Selon une rubrique des problèmes Typescript Github , il s'agit du comportement prévu pour que les classes avec des champs privés conservent leur comportement de typage nominal lorsqu'elles sont émises dans un fichier .d.ts . Malheureusement, le sens de cette explication m'échappe. Existe-t-il une documentation supplémentaire que je peux lire pour mieux comprendre le comportement de frappe nominal de Typescript?


0 commentaires

3 Réponses :


-3
votes

La syntaxe #private est actuellement une proposition de stade 3 pour javascript. Une fois que les navigateurs le prennent en charge, il ne sera pas transcrit par dactylographié.


2 commentaires

La question n'est pas de savoir ce que sont les champs privés. C'est ce qu'est le mystérieux membre #private dans la définition émise.


ahh ... donc il semble que votre question soit à la place "comment puis-je émettre des fichiers de définition dactylographiés rétrocompatibles?"



5
votes

Il rend le type "nominal" de sorte que les autres types qui exposent les mêmes membres publics ne soient pas considérés comme compatibles avec un type qui a un champ privé. Un cas où cela compte est si vous avez un code comme celui-ci:

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

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

Je suis sûr qu'il y a plus d'exemples, mais cette méthode statique est juste celle qui m'est venue à l'esprit.

Cette fonction C.log nécessite une instance réelle de la classe C car elle accède à un champ d'instance de nom privé sur le paramètre d' instance . Si la déclaration emit ne reflète pas que le type C est nominal en indiquant qu'il a un champ privé ES et n'émet à la place que les champs publics, le compilateur utilisera ici des comparaisons de type structurel et ne produira pas les erreurs de type attendues. Par exemple, cette déclaration emit permettrait au code dépendant de passer { bar: 456 } à C.log sans aucune erreur du compilateur.


2 commentaires

En effet j'ai essayé avec une autre classe Test qui compile vers le même .d.ts que votre classe d'exemple (mêmes membres publics, différents membres privés) et quand je tape C.log(new Test); J'obtiens la propriété '#private' dans le type 'Test' fait référence à un membre différent auquel il n'est pas possible d'accéder à partir du type 'C'. . Merci pour votre explication.


J'ai demandé à un modérateur de fusionner ma question avec celle-ci (celle d'origine) afin que vous puissiez être éligible à la prime



1
votes

J'ai essayé de répondre à votre question, mais je n'ai pas pu, puis j'ai posé ma propre question par curiosité, à laquelle un contributeur TypeScript a répondu, vous pouvez trouver sa réponse ici: Quel est le but de #private dans les fichiers de définition TypeScript?

Pour résumer, il y a des cas où les champs privés sont importants pour la comparaison entre les types, c'est pourquoi le champ #private apparaît de sorte que les informations «contient des membres privés» font partie de la définition de type.


0 commentaires