Typescript: 組み込み型を拡張することはできません

作成日 2014年11月14日  ·  14コメント  ·  ソース: microsoft/TypeScript

ES6は、いくつかのネイティブオブジェクトをサブクラス化可能として指定します。たとえば、Object、Boolean、Error、Map、Promiseなどです。 これらの構造がlib.d.tsでモデル化される方法は、変数とインターフェースのペアとして定義されているため、サブクラス化することは不可能です。

ES6仕様セクション19.5.1から:

Errorコンストラクターは、サブクラス化できるように設計されています。 これは、クラス宣言のextends句の値として使用できます。 指定されたError動作を継承することを目的としたサブクラスコンストラクターには、サブクラスインスタンスを初期化するためのErrorコンストラクターへのスーパーコールを含める必要があります。

現在、これにより「クラスは別のクラスのみを拡張できます」というエラーが発生します。

class NotImplementedError extends Error {
    constructor() {
        super("Not Implemented");
    }
}
Bug ES6 Fixed

最も参考になるコメント

今日ではErrorクラスを拡張する機能がありますが、Imはまだノードでそれを使用することに不便を感じています。 たとえば、「error.stack」で問題が発生しています。 throw new Error(...)を実行すると、 error.stack throw new Error(...)を取得しますが、 throw new CustomError()を実行すると、取得しません。 それを解決するために、私はこのトリックを使用することを余儀なくされました:

export class HttpError extends Error {
    httpCode: number;
    message: string;

    constructor(httpCode: number, message?: string) {
        super();
        if (httpCode)
            this.httpCode = httpCode;
        if (message)
            this.message = message;

        this.stack = new Error().stack;
    }
}

全てのコメント14件

これは、サブクラス化できるように意図された関数を公開するjsライブラリの一般的な問題です。 私のライブラリがFooコンストラクターをエクスポートし、Fooのサブクラスのインスタンス(Fooのようなインターフェイスを実装するオブジェクトだけでなく)を受け取ることを期待している場合、現在それを許可する方法はありません。

言いたいのですが

class Error;

このようなステートメントではJavaScriptは発行されず、Typescriptのシンボルテーブルが更新されるだけです。

一般的なケースでは、クラス宣言の本体をセミコロンに置き換えて、次のようなことができるようにしたいと思います。

class Foo<X, Y> extends Bar<X> implements Baz<Y>;

これは、ライブラリがFoo.prototypeがBar.prototypeのインスタンスであり、Bazインターフェイスを実装していることをすでに保証していることを意味します。

@metaweta私はそれがいくつかの冗長性を作るかもしれないと思います:

interface Foo {
}
class Foo; // Foo is now a class!
interface Bar extends Foo {
}
class Bar extends Foo; // Two `extends Foo`s. 

interface X {
}
class X extends Foo; // Hmm?
interface Y extends Foo {
}
class Y; // Hmm?

サブクラス化可能なインターフェイスはオープンエンドクラスとして機能するため、まだいくつかの可能性があります。 #819

@metaweta最初のケースは、アンビエントクラス宣言で処理する必要があります。 例えば

// myLib.d.ts
declare class Error {
}
// no code emitted here for this. 
// myfile.ts
// Error is subclassable,
class MyError extends Error {
}

2番目のケースでは、別の提案の問題を提出します。

ES6仕様の「サブクラス化可能な」オブジェクトの完全なリストは次のとおりです。 現在、これらはすべてインターフェース/変数のペアとして定義されています。

  • オブジェクト
  • ブール値
  • エラー
  • NativeError
  • 日付
  • ストリング
  • RegExp
  • アレイ
  • TypedArray(Int8Array、Uint8Array、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array、Float64Array、およびDataView)
  • 地図
  • セットする
  • WeakMap
  • WeakSet
  • ArrayBuffer
  • DataView
  • 約束する

問題:

  • タイプがクラスとして定義されると、メンバー側を拡張する方法はありません(静的側の拡張はモジュールを介して発生する可能性があります)。
  • クラスを再定義できないため、メンバー側の既存の拡張機能は機能しません(重大な変更)
  • Object、String、Boolean、Date、Number、RegExp、Error、Arrayなどのクラスで関数呼び出しをモデル化する方法はありません。 (再び変化を壊す)

可能な解決策:

  • プロトタイププロパティとコンストラクトシグネチャを使用して任意のタイプを拡張できます
  • クラスのインスタンス側の拡張を許可します(例:モジュールClass.prototype {}、 @ RyanCavanaugh提供
  • クラスコンストラクターでの呼び出しシグネチャの定義を許可する

@mhegazyありがとう、私はアンビエントクラス宣言構造に気づいていませんでした。

この方法で(アンビエントクラス定義を使用して)E​​rrorクラスを拡張すると、ExtendedErrorをスローしても、スタックトレースが生成されず、メッセージが適切に割り当てられません。

declare class Error {
  constructor(message?: string);
}

class ExtendedError extends Error {
  constructor(message?: string) {
    super(message + " extended");
  }
}

//throw new Error("Test");
throw new ExtendedError("Test");

@unionalこれはエンジンの問題のようです。 ES6仕様に従って、動作するはずです。 ES6が承認されたので、エンジンはこれらを修正する必要があります。

私はなんとかそれを機能させることができました。 動作していたjsコードには、Error.captureStackTrace呼び出しがありますが、Error宣言にないため、tsに実装すると削除されます。

サンプルコードがあります:
これは、innerExceptionを受け取るC#のような例外です。

declare class Error {
    public name:string;
    public message:string;
    public stack:string;
    constructor(message?:string);
    static captureStackTrace(error: Error, constructorOpt: any);
}

class Exception extends Error {
    public innerException: Error;
    constructor(message?: string, innerException?: Error|string) {
        // Guard against throw Exception(...) usage.
        if (!(this instanceof Exception)) return new Exception(message, innerException);
        super();
        if (typeof Error.captureStackTrace === 'function') {
            //noinspection JSUnresolvedFunction
            Error.captureStackTrace(this, arguments.callee);
        }
        this.name = "Exception";
        if (innerException) {
            if (innerException instanceof Error) {
                this.innerException = innerException;
                this.message = message + ", innerException: " + this.innerException.message;
            }
            else if (typeof innerException === "string") {
                this.innerException = new Error(innerException);
                this.message = message + ", innerException: " + this.innerException.message;
            }
            else {
                this.innerException = innerException;
                this.message = message + ", innerException: " + this.innerException;
            }
        }
        else {
            this.message = message;
        }
    }
}

#3516で修正されました。 クラスは、コンストラクター関数型の任意の式を拡張できるようになりました。

この修正が出荷されるTypeScriptのリリースは何ですか?
1.5.3と1.5.4のリリースラベルをすばやく確認しましたが、まだ出荷されていないようで、現時点ではマスターのみです。

編集:
申し訳ありませんが、マイルストーン「TypeScript1.6」でマークされたバグに気づきました

お疲れ様でした!

@kostrse npm install -g typescript@next実行すると、TypeScript1.6の夜間リリースを試すことができます。

こんにちは、

Typescript 1.6.2を使用していますが、lib.es6.d.tsにエラー(および配列)が表示されます、...)クラスではなくインターフェイスとして。
これは1.6.2ですでに修正されていますか?

乾杯!

@jpsfsの修正は、 https: //github.com/Microsoft/TypeScript/issues/1168#issuecomment -112955503の@ahejlsbergで指摘されているように、クラスがコンストラクター関数型の任意の式を拡張できるようにすることです。

今日ではErrorクラスを拡張する機能がありますが、Imはまだノードでそれを使用することに不便を感じています。 たとえば、「error.stack」で問題が発生しています。 throw new Error(...)を実行すると、 error.stack throw new Error(...)を取得しますが、 throw new CustomError()を実行すると、取得しません。 それを解決するために、私はこのトリックを使用することを余儀なくされました:

export class HttpError extends Error {
    httpCode: number;
    message: string;

    constructor(httpCode: number, message?: string) {
        super();
        if (httpCode)
            this.httpCode = httpCode;
        if (message)
            this.message = message;

        this.stack = new Error().stack;
    }
}
このページは役に立ちましたか?
0 / 5 - 0 評価