Estoy tratando de lanzar un error personalizado con mi nombre de clase "CustomError" impreso en la consola en lugar de "Error", sin éxito:
class CustomError extends Error {
constructor(message: string) {
super(`Lorem "${message}" ipsum dolor.`);
this.name = 'CustomError';
}
}
throw new CustomError('foo');
La salida es Uncaught Error: Lorem "foo" ipsum dolor
.
Lo que espero: Uncaught CustomError: Lorem "foo" ipsum dolor
.
Me pregunto si eso se puede hacer usando solo TS (sin jugar con los prototipos JS).
fuente
Object.setPrototypeOf(this, this.constructor.prototype)
será no trabajar aquí, la clase tiene que hacer referencia de forma explícita.this.constructor
en este punto, entonces la cadena de prototipos ya estaría intacta.Object.setPrototypeOf(this, new.target.prototype);
typescriptlang.org/docs/handbook/release-notes/…El problema es que la clase incorporada de Javascript
Error
rompe la cadena de prototipos al cambiar el objeto que se va a construir (es decirthis
) a un objeto nuevo y diferente, cuando llamasuper
y ese nuevo objeto no tiene la cadena de prototipos esperada, es decir, es una instancia deError
no deCustomError
.Este problema se puede resolver elegantemente usando 'new.target', que es compatible desde Typecript 2.2, consulte aquí: 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; } } }
El uso
new.target
tiene la ventaja de que no tiene que codificar el prototipo, como algunas otras respuestas propuestas aquí. De nuevo, eso tiene la ventaja de que las clases heredadasCustomError
también obtendrán automáticamente la cadena de prototipos correcta.Si tuviera que codificar el prototipo (p
Object.setPrototype(this, CustomError.prototype)
. Ej. ), ElCustomError
mismo tendría una cadena de prototipos en funcionamiento, pero cualquier clase heredadaCustomError
se rompería, p. Ej., Las instancias de aclass VeryCustomError < CustomError
no serían lasinstanceof VeryCustomError
esperadas, sino únicamenteinstanceof CustomError
.Véase también: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
fuente
this.__proto__
ser público o privado?__proto__
propiedad también está obsoleta y solo un poco estandarizada para ser compatible con versiones anteriores. Consulte MDN para obtener más información sobre ambos.(this as any).__proto__ = actualProto;
. Esto es lo que hice. Hacky, pero no necesito cambiar ninguna declaración de clase.Funciona correctamente en ES2015 ( https://jsfiddle.net/x40n2gyr/ ). Lo más probable es que el problema sea que el compilador de TypeScript se está transfiriendo a ES5 y
Error
no se puede subclasificar correctamente utilizando solo las funciones de ES5; solo se puede subclasificar correctamente usando ES2015 y características superiores (class
o, de manera más oscura,Reflect.construct
). Esto se debe a que cuando llamaError
como una función (en lugar de a través denew
o, en ES2015,super
oReflect.construct
), ignorathis
y crea un nuevoError
.Probablemente tendrá que vivir con la salida imperfecta hasta que pueda apuntar a ES2015 o superior ...
fuente
A partir de TypeScript 2.2 se puede hacer a través de
new.target.prototype
. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#exampleclass CustomError extends Error { constructor(message?: string) { super(message); // 'Error' breaks prototype chain here this.name = 'CustomError'; Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain } }
fuente
Me encontré con el mismo problema en mi proyecto de mecanografiado hace unos días. Para que funcione, utilizo la implementación de MDN usando solo vanilla js. Entonces, su error se vería como lo siguiente:
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');
No parece funcionar en el fragmento de código SO, pero sí en la consola de Chrome y en mi proyecto de mecanografiado:
fuente
strict: true
en tsconfig: stackoverflow.com/questions/43623461/…Literalmente nunca publico en SO, pero mi equipo está trabajando en un proyecto de TypeScript, y necesitábamos crear muchas clases de error personalizadas, mientras también apuntábamos a es5. Habría sido increíblemente tedioso hacer la solución sugerida en cada clase de error. Pero descubrimos que podíamos tener un efecto descendente en todas las clases de error posteriores al crear una clase de error personalizada principal y tener el resto de nuestros errores en
extend
esa clase. Dentro de esa clase de error principal, hicimos lo siguiente para tener ese efecto descendente de actualizar el prototipo:class MainErrorClass extends Error { constructor() { super() Object.setPrototypeOf(this, new.target.prototype) } } class SomeNewError extends MainErrorClass {} ...
El uso
new.target.prototype
fue la clave para actualizar todas las clases de error heredadas sin necesidad de actualizar el constructor de cada una.¡Solo espero que esto le ahorre a otra persona un dolor de cabeza en el futuro!
fuente
Prueba esto...
class CustomError extends Error { constructor(message: string) { super(`Lorem "${message}" ipsum dolor.`) } get name() { return this.constructor.name } } throw new CustomError('foo')
fuente