TypeScriptでメソッドのオーバーロードを行う方法はありますか?


109

TypeScript言語でメソッドのオーバーロードを行う方法はありますか?

私はこのようなことを達成したいです:

class TestClass {
    someMethod(stringParameter: string): void {
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    someMethod(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }
}

var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");

これは私がやりたくない例です(JSのオーバーロードハックのその部分は本当に嫌いです)。

class TestClass {
    private someMethod_Overload_string(stringParameter: string): void {
        // A lot of code could be here... I don't want to mix it with switch or if statement in general function
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    private someMethod_Overload_number_string(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }

    private someMethod_Overload_string_number(stringParameter: string, numberParameter: number): void {
        alert("Variant #3: stringParameter = " + stringParameter + ", numberParameter = " + numberParameter);
    }

    public someMethod(stringParameter: string): void;
    public someMethod(numberParameter: number, stringParameter: string): void;
    public someMethod(stringParameter: string, numberParameter: number): void;

    public someMethod(): void {
        switch (arguments.length) {
        case 1:
            if(typeof arguments[0] == "string") {
                this.someMethod_Overload_string(arguments[0]);
                return;
            }
            return; // Unreachable area for this case, unnecessary return statement
        case 2:
            if ((typeof arguments[0] == "number") &&
                (typeof arguments[1] == "string")) {
                this.someMethod_Overload_number_string(arguments[0], arguments[1]);
            }
            else if ((typeof arguments[0] == "string") &&
                     (typeof arguments[1] == "number")) {
                this.someMethod_Overload_string_number(arguments[0], arguments[1]);
            }
            return; // Unreachable area for this case, unnecessary return statement
        }
    }
}


var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");
testClass.someMethod("string for v#3", 54321);

1
はい、可能性はあります、言語はまだ死んでいません。他に質問は?
2012年

6
@hakre TypeScriptが既にメソッドのオーバーロードをサポートしていることを考えると、これは奇妙なことです。
スビック

@svick:まあ、そのメソッドをオーバーロードと呼んでいますか?あなたの答えでは、メソッド自体はオーバーロードされていません。
ハクレ

2
@hakre仕様では、これをメソッドのオーバーロードと呼んでいます。それは特に素晴らしいバージョンではないと主張することはできますが、まったく存在しないとは言えません。
スビック

@svick:どちらも言わなかった。しかし、OPが尋ねる可能性がメソッドのオーバーロードのメンタルモデルに固有であるように私には思えます。髪の毛の分割については、メソッドシグネチャオーバーロードであると言えます;)
hakre

回答:


165

仕様によると、TypeScriptはメソッドのオーバーロードをサポートしていますが、非常に扱いにくく、パラメータのタイプをチェックする多くの手動作業が含まれています。それは主に、プレーンなJavaScriptでメソッドのオーバーロードにアクセスできる最も近いものがそのチェックを含み、TypeScriptが不必要なランタイムパフォーマンスコストを回避するために実際のメソッド本体を変更しないようにするためだと思います。

私がそれを正しく理解している場合は、最初に各オーバーロードのメソッド宣言を記述してから、引数をチェックして呼び出されたオーバーロードを決定する1つのメソッド実装を記述する必要があります。実装のシグネチャは、すべてのオーバーロードと互換性がある必要があります。

class TestClass {
    someMethod(stringParameter: string): void;
    someMethod(numberParameter: number, stringParameter: string): void;

    someMethod(stringOrNumberParameter: any, stringParameter?: string): void {
        if (stringOrNumberParameter && typeof stringOrNumberParameter == "number")
            alert("Variant #2: numberParameter = " + stringOrNumberParameter + ", stringParameter = " + stringParameter);
        else
            alert("Variant #1: stringParameter = " + stringOrNumberParameter);
    }
}

3
@NicoVanBelle JavaScriptはメソッドのオーバーロードをまったくサポートしていませんよね?では、JSに戻ることはどのように役立つのでしょうか?
9:26にスビック

インターフェースのチェックはどうですか?それ以上の解決策はありませんか:stackoverflow.com/questions/14425568/…
DiPix 2018年

うーん..私は本当にこれが好きではありません、代わりにオプションのパラメーターを持っているだけの方が読みやすいです。
LuckyLikey 2018年

3
ここで重要なのは、大量の型チェックifステートメントがなくてもコードを読みやすく保つことです。それが何に移行するかを気にする人。
Brain2000

34

明確にするために更新します。TypeScriptでのメソッドのオーバーロードは、表現する必要のあるAPIを使用して既存のライブラリのタイプ定義を作成できる限り、便利な機能です。

ただし、独自のコードを記述する場合は、オプションまたはデフォルトのパラメーターを使用して、オーバーロードの認識オーバーヘッドを回避できる場合があります。これは、メソッドオーバーロードのより読みやすい代替手段であり、直感的でない順序付けでオーバーロードを作成することを回避するため、APIを正直に保ちます。

TypeScriptオーバーロードの一般的な法則は次のとおりです。

オーバーロード署名を削除でき、すべてのテストに合格した場合、TypeScriptオーバーロードは必要ありません。

通常、オプションまたはデフォルトのパラメーターを使用して、または共用体タイプを使用して、またはオブジェクト指向を少し使用して、同じことを実現できます。

実際の質問

実際の質問では、次のオーバーロードが求められます。

someMethod(stringParameter: string): void {

someMethod(numberParameter: number, stringParameter: string): void {

現在、個別の実装によるオーバーロードをサポートする言語でも(注:TypeScriptオーバーロードは単一の実装を共有しています)-プログラマーは、順序付けの一貫性を提供するためのアドバイスです。これにより、署名が作成されます。

someMethod(stringParameter: string): void {

someMethod(stringParameter: string, numberParameter: number): void {

stringParameterそれは最初に行くので、常に、必要とされます。あなたはこれを動作するTypeScriptオーバーロードとして書くことができます:

someMethod(stringParameter: string): void;
someMethod(stringParameter: string, numberParameter: number): void;
someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

ただし、TypeScriptオーバーロードの法則に従って、オーバーロードシグネチャを削除しても、すべてのテストに合格します。

someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

実際の質問、実際の順序で

元の順序を維持することが決定された場合、オーバーロードは次のようになります。

someMethod(stringParameter: string): void;
someMethod(numberParameter: number, stringParameter: string): void;
someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

これで、パラメーターを配置する場所を特定するための多くの分岐ができましたが、ここまで読んでいる場合は、この順序を維持したいのですが、ちょっと待って、TypeScriptオーバーロードの法則を適用するとどうなりますか?

someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

十分な分岐

もちろん、行う必要がある型チェックの量を考えると、おそらく2つの方法を用意するのが最善の答えです。

someMethod(stringParameter: string): void {
  this.someOtherMethod(0, stringParameter);
}

someOtherMethod(numberParameter: number, stringParameter: string): void {
  //...
}

これは通常、メソッドのオーバーロードとして知られていません。また、最初のパラメーターのタイプが変わるという質問も参照してください。
12

3
私は私の答えでそれを認めました-オプションのパラメーターを最後に置くので、numberパラメーターは2番目の引数でオプションになります。TypeScriptは「適切な」メソッドオーバーロードをサポートしていませんが、C#の世界でさえ、多くの場合、コードがより読みやすくなるため、オーバーロードからオプションパラメータに移行しています。
Fenton

つまりif (typeof numberParameter != 'undefined')、そうです;)
Juan Mendes、

これは、使用ケース、特にゼロを受け入れるかどうかによって異なります。必要な場合は、!==ジャグリングを避けるために使用することを忘れないでください。
フェントン

1
依存関係注入の機会のように聞こえる@セバスチャン。TypeScriptでのメソッドのオーバーロードには、複数の装飾、デフォルトのパラメーター、および共用体の型を持つ単一のメソッドが含まれるため、より良いエクスペリエンスを提供します。メソッドがより大きく異なる場合は、抽象化を使用するか、複数のメソッドを実装します。
フェントン2017

7

私は望む。この機能も必要ですが、TypeScriptは、オーバーロードされたメソッドを持たない型指定されていないJavaScriptと相互運用できる必要があります。つまり、オーバーロードされたメソッドがJavaScriptから呼び出された場合、1つのメソッド実装にのみディスパッチできます。

codeplexに関するいくつかの関連する議論があります。例えば

https://typescript.codeplex.com/workitem/617

TypeScriptはすべてのifとスイッチングを生成する必要があるので、私はまだそれを行う必要はありません。


2

オプションのプロパティ定義のインターフェイスを関数の引数として使用しないのはなぜですか。

この質問のケースでは、いくつかのオプションプロパティで定義されたインラインインターフェイスを使用すると、以下のようなコードが直接作成されるだけです。

class TestClass {

    someMethod(arg: { stringParameter: string, numberParameter?: number }): void {
        let numberParameterMsg = "Variant #1:";
        if (arg.numberParameter) {
            numberParameterMsg = `Variant #2: numberParameter = ${arg.numberParameter},`;
        }
        alert(`${numberParameterMsg} stringParameter = ${arg.stringParameter}`);
    }
}

var testClass = new TestClass();
testClass.someMethod({ stringParameter: "string for v#1" });
testClass.someMethod({ numberParameter: 12345, stringParameter: "string for v#2" });

TypeScriptで提供されるオーバーロードは他のコメントで述べたように、他の静的言語のような対応する実装コードをサポートせずに、関数のさまざまなシグネチャのリストにすぎないためです。そのため、実装は1つの関数本体でのみ行う必要があります。これにより、Typescriptでの関数のオーバーロードの使用が、実際のオーバーロード機能をサポートする言語ほど快適ではなくなります。

ただし、従来のプログラミング言語では利用できないtypescriptで提供される新しい便利なものがまだ多くあります。匿名インターフェースでのオプションのプロパティサポートは、従来の関数のオーバーロードから快適なゾーンを満たすためのアプローチです。


0

メソッドオーバーロードのバリエーションが多い場合、別の方法は、すべての引数を内部に持つクラスを作成することです。したがって、必要なパラメータのみを任意の順序で渡すことができます。

class SomeMethodConfig {
  stringParameter: string;
  numberParameter: number;

 /**
 *
 */
 constructor(stringParameter: string = '012', numberParameter?: number) { // different ways to make a param optional
   this.numberParameter = 456; // you can put a default value here
   this.stringParameter = stringParameter; // to pass a value throw the constructor if necessary
 }
}

また、デフォルト値を使用してコンストラクタを作成したり、必須の引数を渡すこともできます。次に、次のように使用します:

const config = new SomeMethodConfig('text');
config.numberParameter = 123; // initialize an optional parameter only if you want to do it
this.SomeMethod(config);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.