Dattiloscritto: estensione della class di errore

Sto cercando di generare un errore personalizzato con il mio nome di class “CustomError” stampato nella console anziché “Errore”, senza successo:

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

L’output è Uncaught Error: Lorem "foo" ipsum dolor .

Cosa mi aspetto: Uncaught CustomError: Lorem "foo" ipsum dolor .

Mi chiedo se sia ansible farlo usando solo TS (senza fare scherzi con i prototipi JS)?

Stai usando la versione 2.1 di typescript e la conversione in ES5? Controlla questa sezione della pagina delle modifiche di rottura per eventuali problemi e soluzioni: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array- e-map-may-non-più-lavoro

Il bit pertinente:

Come raccomandazione, è ansible regolare manualmente il prototipo immediatamente dopo ogni super chiamata (…).

 class FooError extends Error { constructor(m: string) { super(m); // Set the prototype explicitly. Object.setPrototypeOf(this, FooError.prototype); } sayHello() { return "hello " + this.message; } } 

Tuttavia, qualsiasi sottoclass di FooError dovrà impostare manualmente anche il prototipo. Per i runtime che non supportano Object.setPrototypeOf, potresti invece essere in grado di usare __proto__ .

Sfortunatamente, queste soluzioni alternative non funzioneranno su Internet Explorer 10 e versioni precedenti. È ansible copiare manualmente i metodi dal prototipo sull’istanza stessa (ad es. FooError.prototype su questo), ma la catena del prototipo stesso non può essere riparata.

Il problema è che l’ Error class built-in di JavaScript spezza la catena del prototipo commutando l’object da build (cioè this ) a un nuovo object diverso, quando chiamate super e quel nuovo object non ha la catena di prototipi prevista, cioè è un’istanza di Error non di CustomError .

Questo problema può essere risolto elegantemente usando ‘new.target’, che è supportato dal Typescript 2.2, vedere qui: 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; } } } 

L’utilizzo di new.target ha il vantaggio che non è necessario codificare il prototipo come alcune altre risposte proposte qui. Ciò ha di nuovo il vantaggio che le classi che ereditano da CustomError otterranno automaticamente anche la catena di prototipi corretta.

Se si dovesse eseguire l’hardcode del prototipo (ad es. Object.setPrototype(this, CustomError.prototype) ), CustomError stesso avrebbe una catena prototipo funzionante, ma tutte le classi che ereditano da CustomError sarebbero danneggiate, ad esempio le istanze di una class VeryCustomError < CustomError non lo farebbe essere instanceof VeryCustomError come previsto, ma solo instanceof CustomError .

Vedi anche: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200

Funziona correttamente in ES2015 ( https://jsfiddle.net/x40n2gyr/ ). Molto probabilmente, il problema è che il compilatore TypeScript sta eseguendo la conversione in ES5 e l’ Error non può essere sottoclass correttamente utilizzando solo le funzionalità ES5; può essere solo correttamente sottoclassi usando ES2015 e sopra le caratteristiche ( class o, più oscuramente, Reflect.construct ). Questo perché quando si chiama Error as a function (piuttosto che tramite new o, in ES2015, super o Reflect.construct ), lo ignora e crea un nuovo Error .

Probabilmente dovrai convivere con l’output imperfetto finché non potrai scegliere come target ES2015 o superiore …

Ho incontrato lo stesso problema nel mio progetto typescript pochi giorni fa. Per farlo funzionare, utilizzo l’implementazione da MDN usando solo vanilla js. Quindi il tuo errore sarebbe simile al seguente:

 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');