【问题标题】:How do I extend a host object (e.g. Error) in TypeScript如何在 TypeScript 中扩展宿主对象(例如错误)
【发布时间】:2012-10-06 14:16:49
【问题描述】:

我想将主机对象Error 扩展为自定义UploadError 类。以下示例在我编译时失败:

class UploadError extends Error {
    constructor(message: string, private code: number) {
        super(message);
    }

    getCode(): number {
        return this.code;
    }
}

当我运行 TypeScript 编译器 tsc 时,我收到以下错误:

UploadError.ts(1,0): A export class may only extend other classes, Error is an interface.

似乎Error 被定义为一个接口。如果有人知道实现的名称是什么,那会让我很高兴:-)

更新:我想使用 Typescripts 继承,而不是像我目前用来解决这个问题的原型继承:

function UploadError (message: string, code: number) {
    this.message = message;
    this.code = code;
}

UploadError.prototype = new Error();

UploadError.prototype.constructor = UploadError;

UploadError.prototype.getCode = (): number => {
    return this.code;
}

【问题讨论】:

    标签: inheritance typescript


    【解决方案1】:

    扩展接口是记录在 here 中的一项重大更改。

    解决方案:在构造函数中手动更改原型。

    class MyError extends Error {
        constructor(m: string) {
            super(m);
    
            // Set the prototype explicitly. If you skip this, iinstanceof will not work :-(
            (<any>this).__proto__ = MyError.prototype;
        }
    }
    
    console.log("Instance of works now: "+(new MyError("my error") instanceof MyError));
    

    【讨论】:

    • 这是在 Node.js 6.9.4 上为我工作的解决方案。有趣的是,与Object.setPrototype 链接的 TypeScript 更改日志中推荐的解决方案不起作用。
    【解决方案2】:

    关注 Typescript 2.1 的新变化 - link

    因此您可以扩展 Error 类,但作为建议,您需要在任何 super(...) 调用后立即手动调整原型:

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

    【讨论】:

      【解决方案3】:

      我使用的是 TypeScript 1.8,但这可能适用于早期版本:

      class MyError extends Error {
      
        static name: string;
      
        constructor(public message?: string, public extra?: number) {
          super(message);
          Error.captureStackTrace(this, MyError);
          this.name = (this as any).constructor.name; // OR (<any>this).constructor.name;
        }
      
      };
      

      请注意,您必须安装 node 类型才能使用 Error.captureStackTrace

      【讨论】:

        【解决方案4】:

        TypeScript 1.6 更新:

        现在可以直接从 Error 类扩展,我原始答案中的代码仍然有效,但不再需要 export declare class Error

        原答案:

        这里的大部分答案都不符合我的要求。自 0.9.5 以来,最初接受的答案不再编译,出现重复标识符异常。而且它们都没有真正的堆栈跟踪(JavaScript 问题,而不是 TypeScript)。

        对我来说,更优雅的解决方案是:

        module YourModule {
            export declare class Error {
                public name: string;
                public message: string;
                public stack: string;
                constructor(message?: string);
            }
        
            export class Exception extends Error {
        
                constructor(public message: string) {
                    super(message);
                    this.name = 'Exception';
                    this.message = message;
                    this.stack = (<any>new Error()).stack;
                }
                toString() {
                    return this.name + ': ' + this.message;
                }
            }
        }
        

        你可以用它做什么:

        • new Exception("msg") instanceof Error == true
        • class SpecificException extends Exception
        • catch (e) { console.log(e.stack); }

        我发现的唯一限制是您必须在模块中声明它,并且不能将它们设为全局。对我来说这不是问题,因为我认为模块有助于结构化,并且它们存在于我制作的任何应用程序中。

        你可以做的一个改进是从堆栈跟踪中去除你的自定义代码,我个人认为堆栈跟踪只是开发人员的眼睛,他们知道去哪里看,所以对我来说没什么大不了的。

        【讨论】:

        • 我认为你可以放心地省略 toString()
        • 你有什么理由想省略它吗?我个人喜欢它提供的“SpecificException: ExceptionMessage”格式。
        • 能否请您在“TypeScript 1.6 更新:”下提供更多详细信息?一个代码示例会很有帮助。
        • @DavidKlempfner 我建议现在使用@Dimitry 的答案。至于 1.6 的更新,它的代码与答案中的代码相同,您只需删除 export declare class Error { … } 子句即可。
        【解决方案5】:

        现在可以扩展 Error 类版本 1.6。请参阅拉取请求允许类扩展子句中的表达式 https://github.com/Microsoft/TypeScript/pull/3516 和问题无法扩展内置类型 https://github.com/Microsoft/TypeScript/issues/1168

        请注意,tsc 不会再抱怨,但您的编辑器/IDE 会一直抱怨,直到更新为止。

        【讨论】:

        【解决方案6】:

        更新

        TypeScript 1.6 带来了扩展原生类型的能力,所以当它落地时你应该可以使用

        class UploadError extends Error {
            //... calls to super and all that jazz
        }
        

        原答案

        您可以在 TypeScript 中实现错误接口,但这不会让您访问super,因为您没有使用继承:

        class UploadError implements Error {
            public name = "CustomError";
        
            constructor (public message: string, private code: number){
        
            }
        }
        
        throw new UploadError ("Something went wrong!", 123);
        

        【讨论】:

          【解决方案7】:

          我找到了解决方案。不是很好,但工作得很好......使用 eval() JS 函数来忽略 TypeScript 检查。

          declare class ErrorClass implements Error {
              public name: string;
              public message: string;
              constructor(message?: string);
          }
          eval('ErrorClass = Error');
          
          export = Exception;
          class Exception extends ErrorClass implements Error { ... }
          

          【讨论】:

            【解决方案8】:

            Ron Buckton 的解决方案在使用 TypeScript 0.8.3 时对我有用,但在 TypeScript 0.9.5 中无法编译。 TypeScript 生成编译错误:Duplicate identifier 'ErrorClass'。我已更改代码以使其再次工作:

            declare class ErrorClass {
                public name: string;
                public message: string;
                constructor(message?: string);
            }
            
            // Move following line to a JavaScript
            // (not TypeScript) file. 
            // var ErrorClass = Error;
            
            class MyError extends ErrorClass {
                public name = "MyError";
                constructor (public message?: string) {
                    super(message);
                }
            }
            

            【讨论】:

              【解决方案9】:

              我知道答案已被接受,而且解决方案绝对令人印象深刻,但我真的不希望我的项目中出现这么多代码只是为了一个例外。

              直到 TypeScript 以某种方式在语言级别获得适当的异常,而 Error 扩展起来非常麻烦,我现在使用以下非常简单的解决方案:

              class MyError {
                  constructor(error: Error) {
                      error.name = this['constructor'].name;
                      error['type'] = this; // for type-checking in exception-handlers
              
                      return error;
                  }
              }
              
              throw new MyError(new Error('aw snap!'));
              

              现在,我的错误类型是真正的类——你可以扩展它们,当抛出未处理的错误时,你会在控制台上看到正确的类名;但我的 Error 对象不是这些类的实例:构造函数确实返回 MyError 的实例,它只是将它自己的名称应用于您传递给它的 Error 实例。

              这也提供了一个简单的解决方法来解决 Error 在您构造它的点而不是在您抛出它的点生成堆栈跟踪的问题 - 因为构造函数签名会强制您构造一个“真正的" 错误实例。

              如果您需要检查异常处理程序中的异常类型,请获取['type'] 属性并使用instanceof 进行比较:

              try {
                  // something throws new MyError(new Error('aw snap!'))
              } catch (error) {
                  console.log(error['type'] instanceof MyError); // => true
              }
              

              这并不理想,但它很简单而且很有效。

              请注意,如果您扩展 MyError,则每次都需要实现构造函数并添加 return super(...),因为 TypeScript 生成的默认构造函数不期望使用返回语句的构造函数。但它确实允许它们。

              【讨论】:

                【解决方案10】:

                我发现以下方法有效:

                declare class ErrorClass implements Error {
                    public name: string;
                    public message: string;
                    constructor(message?: string);
                }
                var ErrorClass = Error;
                
                class MyError extends ErrorClass {
                    public name = "MyError";
                    constructor (public message?: string) {
                        super(message);
                    }
                }
                

                生成的脚本如下:

                var ErrorClass = Error;
                var MyError = (function (_super) {
                    __extends(MyError, _super);
                    function MyError(message) {
                        _super.call(this, message);
                        this.message = message;
                        this.name = "MyError";
                    }
                    return MyError;
                })(ErrorClass);
                

                【讨论】:

                • 对我来说,这会产生编译错误:error TS2000: Duplicate identifier 'ErrorClass'。
                【解决方案11】:

                您可以使用原型添加功能和属性:

                interface Error {
                   code: number;
                   getCode(): number;
                }   
                
                Error.prototype.code = 1;
                
                Error.prototype.getCode = function () {
                    return this.code;
                }  
                var myError = new Error();
                console.log("Code: " + myError.getCode());
                

                使用node.js 运行时,会产生以下输出:

                Code: 1
                

                错误在lib.d.ts中定义如下:

                interface Error {
                    name: string;
                    message: string;
                }
                
                declare var Error: {
                    new (message?: string): Error;
                    (message?: string): Error;
                    prototype: Error;
                }
                

                除此之外,我只看到了定义您自己的扩展Error的接口的明显解决方案:

                interface MyErrorInterface extends Error {       
                    code: number;       
                    getCode(): number;    
                }       
                class MyError implements MyErrorInterface {      
                    code : number;      
                    name : string;      
                    message : string;
                    constructor(code: number, name? : string, message? : string) {
                        this.code = code;
                        this.name = name;
                        this.message = message;
                    }   
                    getCode(): number {
                        return this.code;
                    }   
                }   
                
                var myError = new MyError(1);
                console.log("Code: " + myError.getCode());
                

                【讨论】:

                • 这就是我目前所拥有的。但我没有切换到 TypeScript,所以我仍然可以自己进行继承。
                • 我已经在主帖中添加了我当前的 hack(因为 SO 不允许在评论 #fail 中使用 Markdown)。
                • @MarijnHuizendveld 由于错误是一个接口,恐怕没有其他方法可以做到这一点,我知道。
                • 查看我之前的更新。该代码正在运行,但是,我想要一个更适合 TypeScript 而更少 JavaScript 的解决方案......
                猜你喜欢
                • 2012-12-06
                • 2020-04-17
                • 2018-06-08
                • 2023-02-25
                • 2011-12-12
                • 2016-05-06
                • 2021-06-02
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多