ES6 구문 및 Babel을 사용하여 Javascript에서 오류 확장


132

ES6 및 Babel에서 오류를 확장하려고합니다. 작동하지 않습니다.

class MyError extends Error {
  constructor(m) {
    super(m);
  }
}

var error = new Error("ll");
var myerror = new MyError("ll");
console.log(error.message) //shows up correctly
console.log(myerror.message) //shows empty string

Error 객체는 올바른 메시지 세트를 얻지 못합니다.

Babel REPL에서 시도하십시오 .

이제 SO에 대한 몇 가지 솔루션 ( 예 : here )을 보았지만 모두 매우 ES6이 아닌 것처럼 보입니다. ES6 방식으로 좋은 방법은 무엇입니까? (바벨에서 일하고 있습니다)


2
Babel REPL에 대한 귀하의 링크를 따르면 현재 올바르게 작동하는 것으로 보입니다. 나는 그것이 수정 된 바벨의 버그라고 생각합니다.
kybernetikos

답변:


188

Karel Bílek의 답변에 따라 constructor다음과 같이 약간 변경했습니다 .

class ExtendableError extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
    if (typeof Error.captureStackTrace === 'function') {
      Error.captureStackTrace(this, this.constructor);
    } else { 
      this.stack = (new Error(message)).stack; 
    }
  }
}    

// now I can extend

class MyError extends ExtendableError {}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);

이것은 MyErrorgeneric이 아닌 스택에 인쇄 됩니다 Error.

또한 Karel의 예제에서 누락 된 오류 추적을 스택 추적에 추가합니다.

사용 captureStackTrace가능한 경우 에도 사용 됩니다.

Babel 6을 사용하려면 변환 내장 확장 ( npm )이 필요합니다.


1
@MichaelYounkin if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor.name) } else { this.stack = (new Error(message)).stack; } . 이 함수가 사용 가능한 경우이 함수를 사용하는 것이 더 낫다고 주장합니다. 더 '네이티브'호출 스택을 제공하고 오류 객체의 이름을 인쇄하기 때문입니다. 물론 서버 측에서만 이것을 사용한다면 (노드), 문제도 아닙니다.
리 벤슨

4
@MichaelYounkin 나는 이것이 공감대를 가질 가치가 있다고 생각하지 않습니다. OP는 ES6의 오류 확장에 대해 이야기했습니다. 이 논리에 따라 거의 모든 ES6이 하나 이상의 브라우저에서 누락되었습니다. 내 기능 (기능 검사가 추가됨)은 가장 널리 사용되는 브라우저에서 기본 적용 범위를 제공하고 다른 대체 기능을 제공하며 Node.js에서 100 % 적용 범위를 제공합니다. 클래스 이름이 일관되게 오류가 발생 this.stack = (new Error(message)).stack하면 ...하지만 실제로 이것은 큰 문제가 아니라는 데 동의합니다 .
Lee Benson

6
Babel 6에서는 작동하지 않습니다.new MyError('foo') instanceof MyError === false
Sukima

5
이 코드는 babel을 NPM 모듈로 미리 컴파일했습니다. extendable-error-class npmjs.com/package/extendable-error-class 는 babel-plugin-transform-builtin-extend에 대한 의존성을 피하는 데 편리합니다
12

3
this.message = message;와 중복super(message);
mathieug

39

이 답변 , 이 답변이 코드를 결합 하여이 작은 "도우미"클래스를 만들었습니다.

class ExtendableError extends Error {
  constructor(message) {
    super();
    this.message = message; 
    this.stack = (new Error()).stack;
    this.name = this.constructor.name;
  }
}    

// now I can extend

class MyError extends ExtendableError {
  constructor(m) {   
    super(m);
  }
}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);

REPL 시도


1
this.stack = (new Error(message)).stack;-그렇지 않으면 메시지가 스택 트레이스에서 빠짐
Lee Benson

3
다음과 같은 이유로 필요에 따라 작동하지 않는 것 같습니다. console.log (myerror instanceof ExtendableError); 그것은 여전히 ​​거짓이라고 말합니다.
Mauno Vähä

4
instanceof CustomError를 사용하는 것과 동일한 문제가 작동하지 않습니다. instanceof를 사용할 수 없으면 확장되는 점은 무엇입니까?
gre

message오류 스택 생성자에를 추가하여 향상시킬 수 있으므로 , 던져 질 때 스택 맨 위에 올바른 메시지가 표시됩니다.this.stack = (new Error(message)).stack;
Sebastien

1
myerror.name이제 "오류"를 반환합니다. 이것이 babel의 이후 버전과 관련이 있는지 확실하지 않습니다. 아래 @sukima의 답변 참조
Eric H.

27

마침내 이것을 쉬게했다. 바벨 6 개발자가 명시 적입니다 지원하지 않는 내장에서 연장.이 트릭은 있지만 하지 않을 것 도움이 좋아 Map, Set이에 대한 작업을 수행 등 Error. 예외를 던질 수있는 언어의 핵심 아이디어 중 하나는 맞춤 오류를 허용하는 것이므로 중요합니다. 오류거부 하도록 설계되었으므로 약속이 더 유용 해지기 때문에 이것은 두 가지 중요합니다 .

슬픈 사실은 여전히 ​​ES2015 에서이 방법을 수행해야한다는 것입니다.

Babel REPL의 예

맞춤 오류 패턴

class MyError {
  constructor(message) {
    this.name = 'MyError';
    this.message = message;
    this.stack = new Error().stack; // Optional
  }
}
MyError.prototype = Object.create(Error.prototype);

반면에 Babel 6 용 플러그인이 있습니다.

https://www.npmjs.com/package/babel-plugin-transform-builtin-extend

업데이트 : (2016-09-29 기준) 일부 테스트 후 babel.io가 모든 어설 션을 올바르게 설명하지 않는 것으로 보입니다 (사용자 지정 확장 오류에서 끝남). 그러나 Ember.JS에서 확장 오류가 예상대로 작동합니다 : https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce


예, 연결된 babel 플러그인을 사용하면 허용되는 답변과 올바르게 작동합니다. (그러나 결과 파일은 Reflect가 없기 때문에 Node에서 작동하지 않습니다.)
Karel Bílek

호기심과 마찬가지로 ES2016 사양에 내장이 확장 가능하다고 말하면 v8 및 Babel의 es5와 같은 vms가 왜 그렇게 변환됩니까? 프로토 타입 체인이 다른 프로토 타입에서 가져올 수있는 것과 같은 방식으로 클래스가 클래스를 확장 할 수 있다는 기대는 합리적이지 않습니까? 플러그인에 그러한 의식이 필요한 이유는 무엇입니까?
Sukima

대부분의 유스 케이스가 동작을 공유하는 간단한 객체를 만들고 싶을 때 특히 실망 스럽습니다. 사용할 수있는 맞춤 오류입니다 Error.toString(). 이를 달성하기 위해 특별한 후프와 회전을 수행해야한다는 것은 대부분의 개발자가 그것을 피하고 오류 대신 문자열을 던지는 것과 같은 나쁜 습관에 의지한다는 것을 의미합니다. 또는 객체처럼 자체지도를 만들 수 있습니다. 왜 그러한 OOP 방법을 억제해야합니까?
Sukima

내 생각에 그들은 반대하지 않으며 단지 기술적 인 문제입니다. 그래도 확실하지 않습니다! 당신은 그들에게 물어볼 수 있습니다 :) 프로젝트는 꽤 열려 있습니다
Karel Bílek

지금까지 주제에 대한 간단한 질문의 모든 답변은 "바벨이 지원하지 않습니다"에 남아 있습니다. 나는 이것이 대화의 끝이라고 생각했습니다. 내 소고기는 지원이 부족하여 일반적인 OOP 관용구가 어려워지고 심지어 보일러 플레이트 부스러기를 극복하기 위해 동료와 싸워야했습니다. 여기에 깨끗한 대안이 있었으면 좋겠다. 플러그인을 추가하는 것이 최선의 선택 인 것 같습니다.
Sukima

15

편집 : Typescript 2.1의 주요 변경 사항

오류, 배열 및 맵과 같은 기본 제공 확장 기능이 더 이상 작동하지 않을 수 있습니다.

권장 사항으로, super (...) 호출 직후 프로토 타입을 수동으로 조정할 수 있습니다.

Lee Benson 원래 답변을 편집하면 약간 효과적입니다. 또한 인스턴스 stackExtendableError클래스의 메소드를 추가 하고 추가 합니다.

class ExtendableError extends Error {
   constructor(message) {
       super(message);
       Object.setPrototypeOf(this, ExtendableError.prototype);
       this.name = this.constructor.name;
   }
   
   dump() {
       return { message: this.message, stack: this.stack }
   }
 }    

class MyError extends ExtendableError {
    constructor(message) {
        super(message);
        Object.setPrototypeOf(this, MyError.prototype);
    }
}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror.dump());
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);

1
당신은 호출 할 필요가 Object.setPrototypeOfMyError또한 생성자입니다. stackoverflow.com/a/41102306/186334 github.com/Microsoft/TypeScript-wiki/blob/master/…
CallMeLaNN 1

10

babel 6의 최신 변경 사항으로 인해 변형 내장 확장 프로그램이 더 이상 작동하지 않습니다. 이 혼합 접근법을 사용하여 끝났습니다.

export default class MyError {
    constructor (message) {
        this.name = this.constructor.name;
        this.message = message;
        this.stack = (new Error(message)).stack;
    }
}

MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

import MyError from './MyError';

export default class MyChildError extends MyError {
    constructor (message) {
        super(message);
    }
}

결과적으로 이러한 모든 테스트는 통과합니다.

const sut = new MyError('error message');
expect(sut.message).toBe('error message');
expect(sut).toBeInstanceOf(Error);
expect(sut).toBeInstanceOf(MyError);
expect(sut.name).toBe('MyError');
expect(typeof sut.stack).toBe('string');

const sut = new MyChildError('error message');
expect(sut.message).toBe('error message');
expect(sut).toBeInstanceOf(Error);
expect(sut).toBeInstanceOf(MyError);
expect(sut).toBeInstanceOf(MyChildError);
expect(sut.name).toBe('MyChildError');
expect(typeof sut.stack).toBe('string');

6

인용

class MyError extends Error {
  constructor(message) {
    super(message);
    this.message = message;
    this.name = 'MyError';
  }
}

전화 this.stack = (new Error()).stack;덕분 에 트릭이 필요하지 않습니다 super().

위의 코드는 Babel 에서 this.stack = (new Error()).stack;또는 Error.captureStackTrace(this, this.constructor.name);호출 되지 않으면 스택 추적을 출력 할 수 없지만 . IMO, 여기에 하나의 문제가 있습니다.

사실, 스택 추적에서 출력을 할 수 있습니다 Chrome consoleNode.js v4.2.1이 코드 조각으로.

class MyError extends Error{
        constructor(msg) {
                super(msg);
                this.message = msg;
                this.name = 'MyError';
        }
};

var myerr = new MyError("test");
console.log(myerr.stack);
console.log(myerr);

의 출력 Chrome console.

MyError: test
    at MyError (<anonymous>:3:28)
    at <anonymous>:12:19
    at Object.InjectedScript._evaluateOn (<anonymous>:875:140)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)
    at Object.InjectedScript.evaluate (<anonymous>:664:21)

출력 Node.js

MyError: test
    at MyError (/home/bsadmin/test/test.js:5:8)
    at Object.<anonymous> (/home/bsadmin/test/test.js:11:13)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:467:10)
    at startup (node.js:134:18)
    at node.js:961:3

4

@zangw 답변 외에도 다음과 같이 오류를 정의 할 수 있습니다.

'use strict';

class UserError extends Error {
  constructor(msg) {
    super(msg);
    this.name = this.constructor.name;
  }
}

// define errors
class MyError extends UserError {}
class MyOtherError extends UserError {}

console.log(new MyError instanceof Error); // true

throw new MyError('My message');

올바른 이름, 메시지 및 스택 추적이 발생합니다.

MyError: My message
    at UserError (/Users/honzicek/Projects/api/temp.js:5:10)
    at MyError (/Users/honzicek/Projects/api/temp.js:10:1)
    at Object.<anonymous> (/Users/honzicek/Projects/api/temp.js:14:7)
    at Module._compile (module.js:434:26)
    at Object.Module._extensions..js (module.js:452:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:475:10)
    at startup (node.js:117:18)
    at node.js:951:3

4
작동하지 않습니다 : new MyError('foo') instanceof MyError === false.
Sukima

1
Node.js v7.7.3있습니다.
Gunar Gessner

2

ES6으로 오류를 확장하려고합니다.

class MyError extends Error {…} 구문이 맞습니다.

트랜스 파일러에는 여전히 내장 객체에서 상속하는 데 문제가 있습니다. 귀하의 경우

var err = super(m);
Object.assign(this, err);

문제를 해결하는 것 같습니다.


진실! 그러나 메시지는 어쨌든 설정되지 않았습니다. 새로운 예를 작성하겠습니다.
Karel Bílek

나는 예를 다시 썼다
Karel Bílek


"super (m)"은 빈 객체를 반환합니다. 따라서 Object.assign이 도움이되지 않습니다.
Karel Bílek 2016 년

@ KarelBílek : 어떤 브라우저를 사용하고 있습니까? Error.call()나를 위해 새로운 오류 인스턴스를 반환합니다.
Bergi

2

이것이 받아 들여진 대답이 더 이상 작동하지 않으면 항상 공장을 대안 ( repl ) 으로 사용할 수 있습니다 .

function ErrorFactory(name) {
   return class AppError extends Error {
    constructor(message) {
      super(message);
      this.name = name;
      this.message = message; 
      if (typeof Error.captureStackTrace === 'function') {
        Error.captureStackTrace(this, this.constructor);
      } else { 
        this.stack = (new Error(message)).stack; 
      }
    }
  }     
}

// now I can extend
const MyError = ErrorFactory("MyError");


var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);


필요한 babel 플러그인이 있으면 허용 된 답변이 여전히 작동합니다. 그래도이 답변에 감사드립니다!
Karel Bílek

2

위에서 설명한 것보다 더 강력한 구문을 선호합니다. 오류 유형의 추가 방법은 예쁘 console.log거나 다른 것을 만드는 데 도움이됩니다 .

export class CustomError extends Error {
    /**
     * @param {string} message
     * @param {number} [code = 0]
     */
    constructor(message, code = 0) {
        super();

        /**
         * @type {string}
         * @readonly
         */
        this.message = message;

        /**
         * @type {number}
         * @readonly
         */
        this.code = code;

        /**
         * @type {string}
         * @readonly
         */
        this.name = this.constructor.name;

        /**
         * @type {string}
         * @readonly
         */
        this.stack = CustomError.createStack(this);
    }

    /**
     * @return {string}
     */
    toString() {
        return this.getPrettyMessage();
    }

    /**
     * @return {string}
     */
    getPrettyMessage() {
        return `${this.message} Code: ${this.code}.`;
    }

    /**
     * @param {CustomError} error
     * @return {string}
     * @private
     */
    static createStack(error) {
        return typeof Error.captureStackTrace === 'function'
            ? Error.captureStackTrace(error, error.constructor)
            : (new Error()).stack;
    }
}

이 코드를 테스트하기 위해 비슷한 것을 실행할 수 있습니다.

try {
    throw new CustomError('Custom error was thrown!');
} catch (e) {
    const message = e.getPrettyMessage();

    console.warn(message);
}

CustomError유형의 확장을 환영합니다. 특정 유형의 기능을 확장 유형에 추가하거나 기존 기능을 대체 할 수 있습니다. 예를 들어.

export class RequestError extends CustomError {
    /**
     * @param {string} message
     * @param {string} requestUrl
     * @param {number} [code = 0]
     */
    constructor(message, requestUrl, code = 0) {
        super(message, code);

        /**
         * @type {string}
         * @readonly
         */
        this.requestUrl = requestUrl;
    }

    /**
     * @return {string}
     */
    getPrettyMessage() {
        const base = super.getPrettyMessage();

        return `${base} Request URL: ${this.requestUrl}.`;
    }
}

1

@sukima가 언급했듯이 네이티브 JS를 확장 할 수 없습니다. OP의 질문에 대답 할 수 없습니다.

Melbourne2991의 답변 과 비슷하게 팩토리를 사용했지만 고객 오류 유형에 대한 MDN의 권장 사항을 따랐습니다 .

function extendError(className){
  function CustomError(message){
    this.name = className;
    this.message = message;
    this.stack = new Error().stack; // Optional
  }
  CustomError.prototype = Object.create(Error.prototype);
  CustomError.prototype.constructor = CustomError;
  return CustomError;
}

1

이것은 나를 위해 작동합니다 :

/**
 * @class AuthorizationError
 * @extends {Error}
 */
export class AuthorizationError extends Error {
    message = 'UNAUTHORIZED';
    name = 'AuthorizationError';
}

0

Babel을 사용하지 않지만 일반 ES6에서는 다음이 잘 작동하는 것 같습니다.

class CustomError extends Error {
    constructor(...args) {
        super(...args);
        this.name = this.constructor.name;
    }
}

REPL에서 테스트 :

> const ce = new CustomError('foobar');
> ce.name
'CustomError'
> ce.message
'foobar'
> ce instanceof CustomError
true
> ce.stack
'CustomError: foobar\n    at CustomError (repl:3:1)\n ...'

보시다시피, 스택에는 오류 이름과 메시지가 모두 포함됩니다. 누락 된 것이 있는지 확실하지 않지만 다른 모든 대답은 문제를 복잡하게하는 것으로 보입니다.


0

@Lee Benson의 솔루션을 다음과 같이 조금 개선했습니다.

extendableError.js

class ExtendableError extends Error {
    constructor(message, errorCode) {
        super(message);
        this.name = this.constructor.name;
        this.errorCode = errorCode
        if (typeof Error.captureStackTrace === 'function') {
            Error.captureStackTrace(this, this.constructor);
        } else {
            this.stack = (new Error(message)).stack;
        }
    }


}

export default ExtendableError

오류의 예

import ExtendableError from './ExtendableError'

const AuthorizationErrors = {
    NOT_AUTHORIZED: 401,
    BAD_PROFILE_TYPE: 402,
    ROLE_NOT_ATTRIBUTED: 403
}

class AuthorizationError extends ExtendableError {
    static errors = AuthorizationErrors 
}

export default AuthorizationError 

그런 다음 옵션 지정자가있는 동안 오류를 그룹화하여 일부 애플리케이션 특정 상황에서 다르게 수행 할 작업을 결정할 수 있습니다.

new AuthorizationError ("The user must be a seller to be able to do a discount", AuthorizationError.errors.BAD_PROFILE_TYPE )
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.