125 votes

Typescript - Extension de la classe d'erreur

J'essaie de lancer une erreur personnalisée avec le nom de ma classe "CustomError" imprimé dans la console au lieu de "Error", sans succès :

class CustomError extends Error { 
    constructor(message: string) {
      super(`Lorem "${message}" ipsum dolor.`);
      this.name = 'CustomError';
    }
}
throw new CustomError('foo'); 

Le résultat est Uncaught Error: Lorem "foo" ipsum dolor .

Ce que j'attends : Uncaught CustomError: Lorem "foo" ipsum dolor .

Je me demande si cela peut être fait en utilisant uniquement TS (sans s'embêter avec les prototypes JS) ?

112voto

Vidar Points 638

Utilisez-vous Typescript version 2.1, et transpilez-vous vers ES5 ? Consultez cette section de la page des changements récents pour connaître les problèmes éventuels et les solutions de rechange : https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work

Le passage pertinent :

Il est recommandé d'ajuster manuellement le prototype immédiatement après tout appel à super(...).

class FooError extends Error {
    constructor(m: string) {
        super(m);

        // Set the prototype explicitly.
        Object.setPrototypeOf(this, FooError.prototype);
    }

    sayHello() {
        return "hello " + this.message;
    }
}

Cependant, toute sous-classe de FooError devra également définir manuellement le prototype. Pour les environnements d'exécution qui ne prennent pas en charge Object.setPrototypeOf, vous pouvez utiliser la méthode suivante __proto__ .

Malheureusement, ces solutions de contournement ne fonctionneront pas sur Internet Explorer 10 et antérieur. Il est possible de copier manuellement les méthodes du prototype sur l'instance elle-même (par exemple, FooError.prototype sur this), mais la chaîne du prototype elle-même ne peut pas être corrigée.

99voto

Kristian Hanekamp Points 325

Le problème est que la classe intégrée de Javascript Error rompt la chaîne du prototype en changeant l'objet à construire (c.-à-d. this ) à un nouvel objet différent, lorsque vous appelez super et ce nouvel objet n'a pas la chaîne de prototypes attendue, c'est-à-dire que c'est une instance de Error pas de CustomError .

Ce problème peut être résolu de manière élégante en utilisant 'new.target', qui est supporté depuis Typescript 2.2, voir ici : https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html

class CustomError extends Error {
  constructor(message?: string) {
    // 'Error' breaks prototype chain here
    super(message); 

    // restore prototype chain   
    const actualProto = new.target.prototype;

    if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); } 
    else { this.__proto__ = actualProto; } 
  }
}

Utilisation de new.target a l'avantage de ne pas devoir coder en dur le prototype, comme le proposaient certaines autres réponses ici. Cela présente également l'avantage que les classes héritant de CustomError obtiendra automatiquement la chaîne de prototypes correcte.

Si vous deviez coder en dur le prototype (par ex. Object.setPrototype(this, CustomError.prototype) ), CustomError lui-même aurait une chaîne de prototypes fonctionnelle, mais toutes les classes héritant de CustomError seraient rompus, par exemple les instances d'une class VeryCustomError < CustomError ne serait pas instanceof VeryCustomError comme prévu, mais seulement instanceof CustomError .

Voir aussi : https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200

29voto

T.J. Crowder Points 285826

Il fonctionne correctement dans ES2015 ( https://jsfiddle.net/x40n2gyr/ ). Il est fort probable que le problème soit dû au fait que le compilateur TypeScript effectue une transposition vers ES5, et que Error ne peut pas être correctement sous-classée en utilisant uniquement les fonctionnalités ES5 ; elle ne peut être correctement sous-classée qu'en utilisant les fonctionnalités ES2015 et supérieures ( class ou, de façon plus obscure, Reflect.construct ). En effet, lorsque vous appelez Error comme une fonction (plutôt que via new ou, dans l'ES2015, super ou Reflect.construct ), il ignore this et crée un nouveau Error .

Vous devrez probablement vous contenter d'un résultat imparfait jusqu'à ce que vous puissiez cibler ES2015 ou une version plus récente...

7voto

Mμ. Points 4343

J'ai rencontré le même problème dans mon projet typescript il y a quelques jours. Pour le faire fonctionner, j'utilise l'implémentation de MDN en utilisant seulement vanilla js. Votre erreur ressemblerait donc à ce qui suit :

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || 'Default Message';
  this.stack = (new Error()).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;

throw new CustomError('foo');

Cela ne semble pas fonctionner dans l'extrait de code SO, mais cela fonctionne dans la console chrome et dans mon projet typescript :

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X