2016-12-12 35 views
17

próbuję rzucić niestandardowego błędu z mojej „CustomErrors” nazwa klasy drukowanej w konsoli zamiast „error”, bez powodzenia:Maszynopis - Rozszerzanie klasy Error

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

Wyjście jest Uncaught Error: Lorem "foo" ipsum dolor.

Czego się spodziewam: Uncaught CustomError: Lorem "foo" ipsum dolor.

Zastanawiam się, czy można to zrobić za pomocą tylko TS (bez naruszania prototypów JS)?

+1

Co 'ParameterUndefinedError'? –

+0

@NitzanTomer To typo. Naprawiłem to, dzięki. – darksoulsong

Odpowiedz

16

Używasz wersji 2.1, maszynopis i transpiling do ES5? Sprawdź ten fragment strony łamanie zmian dla ewentualnych problemów i rozwiązania: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work

Odpowiedni bit:

Jako zalecenie, można ręcznie ustawić prototyp natychmiast po każdej Super (...) wywołuje.

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

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

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

Jednak każda podklasa FooError będzie również musiała ręcznie ustawić prototyp. W przypadku środowisk wykonawczych, które nie obsługują Object.setPrototypeOf, można zamiast tego używać __proto__.

Niestety, obejścia te nie będą działać w przeglądarce Internet Explorer 10 i wcześniejszych wersjach. Można ręcznie skopiować metody z prototypu na samą instancję (np. FooError.prototype), ale samego łańcucha prototypów nie można naprawić.

+1

Tak, transponuję do ES5. Próbowałem ustawić prototyp, tak jak sugerowałeś, ale niestety to nie zadziałało. – darksoulsong

+1

Warto zauważyć, że niestety, 'Object.setPrototypeOf (this, this.constructor.prototype)' _nie_ tutaj działa, klasa musi być jawnie odwoływana. –

+1

@JohnWeisz, jeśli poprawka, o której wspomniałeś, działała, to na początku nie będzie niczego, co można by naprawić, gdybyś mógł przeczytać prototyp z 'this.constructor' w tym momencie, łańcuch prototypów byłby już nienaruszony. – thetrompf

5

Działa poprawnie w ES2015 (https://jsfiddle.net/x40n2gyr/). Najprawdopodobniej problem polega na tym, że kompilator TypeScript jest transponowany do ES5, a Error nie może być poprawnie podklasowany przy użyciu tylko funkcji ES5; może być poprawnie podklasowana tylko przy użyciu ES2015 i wyżej wymienionych funkcji (class lub, co bardziej mylne, Reflect.construct). To dlatego, że podczas rozmowy Error jako funkcja (raczej niż poprzez new lub w ES2015, super lub Reflect.construct), ignoruje this i tworzy nowyError.

Prawdopodobnie będziesz musiał żyć z wyjściem niedoskonałej aż można kierować ES2015 lub wyższy ...

6

Wpadłem na ten sam problem w moim projekcie na maszynopis kilka dni temu. Aby to działało, używam implementacji od MDN używając tylko wanilii js. Więc twój błąd będzie wyglądał tak:

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

nie wydaje się do pracy w takim fragmencie kodu, ale to nie w konsoli i chrom w moim projekcie maszynopis:

enter image description here

+0

Podobał się pomysł, nie udało się go zaimplementować w pismach maszynowych –

+0

Naprawdę? To działało na mój macierzysty projekt typu maszynopis. –

+0

Cóż, chciałem również odrysować część z tablicy, a prawdopodobnie nie udało mi się tego dokonać, spróbuję ponownie bez innych shinanigenów. –

0

problemem jest to, że JavaScript wbudowanego w klasie Error przerywa łańcuch prototyp przełączając obiekt zostać skonstruowana (czyli this) do nowego, innego obiektu, po wywołaniu super, a nowy obiekt nie ma oczekiwanego łańcucha prototypów, tj.jest to instancja o numerze Error, a nie CustomerError.

Problem ten może być rozwiązany za pomocą elegancko „new.target”, który jest obsługiwany od maszynopis 2.2, zobacz tutaj: 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__ = new.target.prototype; } 
    } 
} 

Korzystanie new.target ma tę zaletę, że nie trzeba hardcode prototyp , podobnie jak inne proponowane tutaj odpowiedzi. To znowu ma tę zaletę, że klasy dziedziczące po CustomError automatycznie uzyskają prawidłowy łańcuch prototypów.

Jeśli było zakodować prototyp (np Object.setPrototype(this, CustomError.prototype)) CustomError byłaby sama mieć działający łańcuch prototypów, ale wszelkie zajęcia dziedziczenie z CustomError zostanie uszkodzony, np wystąpienia class VeryCustomError < CustomError nie będą zgodne z oczekiwaniami, ale tylko instanceof CustomError.

Zobacz także: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200