Typescript: Suggestion: interdire l'utilisation avant la définition

Créé le 15 juil. 2014  ·  29Commentaires  ·  Source: microsoft/TypeScript

Le compilateur doit émettre une erreur lorsque le code utilise des valeurs avant qu'elles ne puissent éventuellement être initialisées.

// Error, 'Derived' declaration must be after 'Base'
class Derived extends Base { }
class Base { }
Bug

Commentaire le plus utile

Juste pour mentionner que nous venons d'être mordus par cela aujourd'hui, et qu'il nous a fallu du temps pour comprendre ce qui se passait.

TypeScript v1.8.10, construction basée sur Webpack, à la fois la classe de base et la classe dérivée définies dans le même fichier, mais (apparemment) dans le mauvais ordre, pas d'erreurs de compilation ni d'avertissements, et même si les cartes sources fonctionnent, la pile d'appels d'erreur pointait vers un emplacement très inutile (la fin d'une autre classe important la classe dérivée).

Ne pas parcourir toute la discussion, mais comme première aide, il semble qu'un avertissement du compilateur aiderait. Juste nos 2 ¢

Tous les 29 commentaires

Bien que lancer une erreur de compilateur soit une bonne solution, le compilateur pourrait peut-être afficher les classes dans le bon ordre. Ce serait une fonctionnalité qui tue. Par exemple, le compilateur garde une trace de la relation de dépendance et génère les classes en fonction de cela, lançant l'erreur du compilateur uniquement lorsqu'il est incapable de résoudre l'ordre de dépendance.

Le compilateur garde une trace de la relation de dépendance et génère les classes en fonction de cela, lançant l'erreur du compilateur uniquement lorsqu'il est incapable de résoudre l'ordre de dépendance.

Devrions-nous en faire une nouvelle suggestion? C'est pourquoi j'utilise actuellement des modules AMD plutôt que des modules internes TypeScript; le compilateur RequireJS détermine l'ordre de sérialisation du module approprié en utilisant les dépendances que je spécifie dans la base de code (en utilisant require() ).

Lien vers # 274. Nous devons définir quelles en seraient les règles et la portée

Le cas extend semble être un bon candidat pour la vérification lexicale; nous devons juste nous assurer que lexicalement la classe de base précède la classe dérivée. Y a-t-il d'autres cas à considérer?

Un problème est que la réorganisation des définitions de classe peut réorganiser silencieusement l'ordre d'initialisation statique. Si la classe de base vient après une classe dérivée, je vote pour maintenir le code d'initialisation statique sur le site de définition de classe ou simplement signaler une erreur.

Je pense que le cas de fichiers multiples est plus intéressant et utile du point de vue d'un grand projet / maintenance (ce qui est l'objectif apparent de dactylographié, après tout).

Donc, je pense que nous devons considérer l'ordre de sortie en mode de sortie à un seul fichier. (ce serait également bien de pouvoir obtenir cet ordre pour créer des fichiers html qui incluent plusieurs fichiers).

Voici quelques déclarations qui, je pense, assureraient la commande:

class X extends Y {} // ensure Y is defined in prior file
module { new X(); } // ensure X is defined in prior file
class S { static z = new Z(); } // ensure Z is defined in prior file

Nous pourrions également étendre cela aux fonctions et aux variables définies avant utilisation, pas seulement aux classes.

PS j'ai un prototype.

Je ne pense pas qu'il y ait une intention de tenter de réorganiser les émissions pour vous, seulement pour donner des erreurs là où nous le pouvons pour des choses qui échoueront sûrement à l'exécution.

Dan, je suis d'accord avec vous sur la réorganisation dans un seul fichier, mais lorsque plusieurs fichiers sont combinés à l'aide de --out, le compilateur a le contrôle sur l'ordre d'émission et je préférerais que l'ordre qu'il choisisse fonctionne.

Les fonctions

pour réorganiser; la philosophie que nous avons suivie est de laisser le code de sortie être aussi proche que possible du code d'entrée. en substance, nous laissons passer le code utilisateur, nous supprimons simplement les types. en ce sens, une erreur serait plus conforme à ce que nous avons fait jusqu'à présent.

En ce qui concerne l'implémentation, j'ai récemment ajouté une vérification d'ordre lexical avec Let et Const, et peut être extraite comme une vérification générale et utilisée pour ces différents cas. Vous pouvez le trouver ici:
https://github.com/Microsoft/TypeScript/blob/master/src/compiler/checker.ts#L329

Nous devons identifier clairement les cas où nous vérifions, et un PR serait certainement le bienvenu :)

Oui, je conviens que nous ne voulons pas réorganiser dans un seul fichier dactylographié, mais dans le cas du fichier --out, l'ordre n'est pas spécifié par l'utilisateur donc, encore une fois, je préférerais que le compilateur fasse de son mieux pour choisissez une commande qui fonctionne.

La fonction de levage est un bon exemple où nous n'avons pas besoin de nous soucier du cas d'un seul fichier, mais où la compilation en plusieurs fichiers et le choix d'une séquence pour les inclure dans un fichier .html peuvent être non triviaux pour un humain. Les variables non définies à l'utilisation sont un excellent exemple de cas où un comportement inattendu peut être introduit par le compilateur en raison d'un changement dans les lignes /// <reference> .

mais dans le cas du fichier --out, l'ordre n'est pas spécifié par l'utilisateur

Ce n'est pas vraiment le cas. Nous avons des règles très simples ici - utilisez l'ordre implicite des balises reference et l'ordre des fichiers sur la ligne de commande. Dans les deux cas, l'utilisateur nous fournit une commande. Le fait que le compilateur ignore l'ordre que l'utilisateur a fourni est une route dangereuse pour descendre. Que faire si le compilateur décide d'un ordre différent de celui que vous préférez? Comment pourriez-vous annuler cela? Et si un ordre casse 2 classes et un autre ordre casse 2 variables?

Alors nous ne devrions pas changer l'ordre mais devrions-nous au moins (avoir la possibilité de) avertir l'utilisateur que l'ordre utilisé par le compilateur est probablement incorrect?

Ouaip. nous ne devons pas commander, mais plutôt une erreur.

Quel est le bon idiome dans TypeScript pour les classes mutuellement récursives? Un declare class avant la définition réelle?

Si vos classes se réfèrent simplement les unes aux autres dans le système de types ou dans les méthodes d'instance, ce n'est pas un problème. Le seul modèle "mutuellement récursif" qui pose problème est celui-ci:

class Alpha {
    static myFriendBeta = new Beta();   
}

class Beta {
    static myFriendAlpha = new Alpha(); 
}

Vous pouvez réécrire ceci en tant que clodule:

class Alpha {
}

class Beta {
    static myFriendAlpha = new Alpha();
}

module Alpha {
    export var myFriendBeta = new Beta();
}

Ok, quelles règles nous aimerions implémenter dans le cadre de ce problème à part «la classe de base devrait être définie lexicalement avant la classe dérivée»?

Interdire les références directes aux membres internes du module, par exemple

var x = M.fn(); // Should error
module M {
    export function fn() {}
}

Interdire les références directes aux membres enum

var x = E.A; // Should error
enum E { A }

IMO, la portée de ce problème est plutôt limitée en l'état car les gens ne définissent normalement pas tout leur code dans un seul fichier: la classe de base est plus susceptible d'exister dans un fichier séparé de la classe dérivée.

Je suggère que ce qui suit de @sparecycles devrait également faire partie de la résolution de ce problème:

Alors nous ne devrions pas changer l'ordre mais devrions-nous au moins (avoir la possibilité de) avertir l'utilisateur que l'ordre utilisé par le compilateur est probablement incorrect?

"L'ordre utilisé par le compilateur" doit inclure l'ordre spécifié dans tsconfig .

Lorsque la classe de base et les classes dérivées sont dans le même fichier, le problème n'est pas aussi grave: le programme plantera au démarrage et le programmeur changera l'ordre dans ce fichier et le problème sera _fixed_.

Le cas de fichiers multiples est l'endroit où le compilateur doit avertir lors de la concaténation de plusieurs fichiers dans un ordre douteux, car cet ordre peut changer pour des raisons subtiles.

Prenons le cas où il existe une classe de base et plusieurs classes dérivées, le tout dans des fichiers séparés. La classe de base utilise certaines des classes dérivées dans son implémentation, et donc elle les référence, mais elle doit toujours être placée en premier dans la sortie. De même, toutes les classes dérivées doivent référencer la classe de base.

Eh bien, il n'y a pas de problème avec les fichiers référencés mutuellement, si A.ts fait référence mutuellement à B.ts et X.ts inclut A.ts, alors l'ordre de sortie sera [B, A, X], et s'il fait référence à B.ts l'ordre sera [A, B, X]. (Mais une seule de ces commandes peut fonctionner à l'exécution.) Cela rend les choses fragiles, car la compilation réussira aussi bien si B ou A est référencé.

Ma solution pour mon problème de système de classes dérivé de base: ajoutez un index.ts pour ma hiérarchie de classes et incluez, dans ce fichier, toutes les classes dérivées, suivies de la classe de base. Cela garantissait que la sortie mettrait la classe de base en premier. (complètement contre-intuitif!). J'ai trouvé que si je référençais directement les fichiers que je voulais, cela finirait par générer la classe de base après celle dérivée.

L'avertissement du compilateur sera très bien, mais ce serait également formidable de pouvoir marquer l'une des références dans un scénario de référence mutuelle comme ordre d'émission, et l'autre est juste pour extraire des déclarations. Des références de commande d'émissions mutuelles seraient une erreur.

(Je l'ai actuellement implémenté pour générer automatiquement la liste des inclusions .js dans notre projet Visual Studio / Typescript car nous utilisons une sortie multi-fichiers (plus facile à déboguer). Mais le code est en C # en tant que tâche en ligne. C'est l'intérêt que je vais demander si je peux le partager. Il s'agit essentiellement de deux exécutions de l'algorithme CC de Tarjan.)

L'avertissement lorsque l'ordre d'émission est erroné et la stabilisation de l'ordre d'émission avec une directive explicite contribueraient grandement à faire du dactylographie un langage viable pour les grands projets ... oui?

Je rencontre ce problème assez fréquemment avec ma base de code plutôt petite (environ 80 fichiers .ts). Idéalement, je voudrais ne pas avoir <reference> balises

Mon application n'a qu'un seul fichier qui instancie les classes et exécute l'application (ma racine de composition), quelques fichiers qui ajoutent des extensions (par exemple en ajoutant Array.prototype.distinct ) et le reste ne sont que des définitions de classe / interface.

Dans ce cas, la majeure partie du code est un jeu équitable pour la réorganisation, et ne devrait pas nécessiter manuellement les définitions <reference> pour bien faire. Je vois les définitions de classe comme un jeu équitable pour toute réorganisation du compilateur, et devraient être shuntées jusqu'en haut de la sortie combinée, tandis que le reste des instructions peut conserver l'ordre tel qu'il était lors de l'entrée.

Un drapeau de compilateur --looseSorting serait-il possible? Cela semble être une fonctionnalité assez recherchée.

Dans la déclaration de classe d'émission dans ES6, nous effectuons une affectation de propriété statique après la déclaration de classe. Cela rend la référence à la propriété statique de classe dans le nom de propriété calculé devient utilisation-avant-définition.

Émis JS:

class C {
    [C.p] () {}  // Use before definition
    [C.p+ C.e]() {}  // Use before definition
    [D.f] () {}  // Use before definition
}
C.p = 10;
C.e = 20;

class D {
}
D.f = "hi";

Nous ne voulons avertir de cette erreur que dans deux cas: les noms de propriété calculés font référence à sa propriété statique de classe, ou font référence à une autre classe, celle définie en dessous, propriété.

L'exemple trivial avec lequel nous jouions aujourd'hui, juste à inclure dans tous les tests:

function f() {
    function g() {
        i = 10;
    }

    let i = 20;
    g();
}

Il serait bon d'obtenir les permutations d'utilisations / définitions de g autour de i .

N'oubliez pas de penser aux fonctions définies dans la portée des blocs. C'est un comportement non défini selon la norme JavaScript, et je sais qu'au moins Firefox et Chrome ne sont pas d'accord sur leur mise en œuvre.

par exemple:

function f() {
    if (true) {
        g(); // iirc, g executes in Chrome, and is undefined in Firefox
        function g() {
        }
        g(); // works in both browsers
    }
}

Juste pour mentionner que nous venons d'être mordus par cela aujourd'hui, et qu'il nous a fallu du temps pour comprendre ce qui se passait.

TypeScript v1.8.10, construction basée sur Webpack, à la fois la classe de base et la classe dérivée définies dans le même fichier, mais (apparemment) dans le mauvais ordre, pas d'erreurs de compilation ni d'avertissements, et même si les cartes sources fonctionnent, la pile d'appels d'erreur pointait vers un emplacement très inutile (la fin d'une autre classe important la classe dérivée).

Ne pas parcourir toute la discussion, mais comme première aide, il semble qu'un avertissement du compilateur aiderait. Juste nos 2 ¢

Je trouve ridicule que TS ne prenne pas en charge cette fonctionnalité hors de la boîte. La confusion qu'elle provoque est similaire à l'utilisation de JS standard. Aussi, des méthodes virtuelles, n'importe qui?

@MrGuardian la repro décrite dans l'OP a été corrigée. Peut-être pouvez-vous clarifier un nouveau problème ou un problème existant qui décrit mieux le problème que vous rencontrez?

(# 12673) voici deux autres cas où l'OMI devrait être des erreurs:

`` ``
Test de classe
{
_b = ceci._a; // indéfini, pas d'erreur / d'avertissement
_a = 3;

static _B = Test._A; // undefined, no error/warning
static _A = 3;

method()
{
    let a = b; // Block-scoped variable 'b' used before its declaration
    let b = 3;
}

}
`` ``

@Spongman pouvez-vous enregistrer cela dans un autre numéro s'il vous plaît? Merci!

12673

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

bgrieder picture bgrieder  ·  3Commentaires

siddjain picture siddjain  ·  3Commentaires

weswigham picture weswigham  ·  3Commentaires

fwanicka picture fwanicka  ·  3Commentaires

kyasbal-1994 picture kyasbal-1994  ·  3Commentaires