TypeScript関数のオーバーロード


244

TypeScript言語仕様のセクション6.3では、関数のオーバーロードについて説明し、これを実装する方法の具体例を示しています。しかし、私がこのようなことをしようとすると:

export class LayerFactory { 

    constructor (public styleFactory: Symbology.StyleFactory) { }

    createFeatureLayer (userContext : Model.UserContext, mapWrapperObj : MapWrapperBase) : any {           
         throw "not implemented";
    }                 

    createFeatureLayer(layerName : string, style : any) : any {
        throw "not implemented";
     }        

}

関数パラメーターの型が異なる場合でも、重複した識別子を示すコンパイラエラーが発生します。2番目のcreateFeatureLayer関数にパラメーターを追加しても、コンパイラエラーが発生します。アイデアをお願いします。


回答:


189

これは、両方の関数がJavaScriptにコンパイルされている場合、それらのシグネチャが完全に同一であるためと考えられます。JavaScriptにはタイプがないため、同じ数の引数を取る2つの関数を作成することになります。したがって、TypeScriptは、そのような関数の作成を制限します。

TypeScriptはパラメーターの数に基づくオーバーロードをサポートしますが、オブジェクト指向言語と比較すると、従うべき手順は少し異なります。別のSOの質問に答えて、誰かがいい例で説明しました:メソッドのオーバーロード?

基本的には、TypeScriptでコンパイルエラーが発生しないように、関数と宣言を1つだけ作成しています。このコードをJavaScriptにコンパイルすると、具象関数のみが表示されます。JavaScript関数は複数の引数を渡すことで呼び出すことができるため、機能します。


50
これをサポートするように言語を修正することができます。理論的には、別の名前が付けられ、コンパイルされたTypeScript(createFeatureLayer_1やcreateFeatureLayer_2など)によって呼び出される関数実装を生成し、createFeatureLayerは、バニラJavaScriptとの相互運用のための引数の内容に基づいて、どちらを呼び出すかを決定できます。
トーマスS.トリアス2014年

8
TypeScriptでのオーバーロードはパラメーターの数に基づいてのみ可能であるかのように言いますが、Steve Fentonの回答に示されているように、タイプに基づくオーバーロードも可能です。
Matthijs Wessels 2014

9
これはちょっと不自由です。TypeScriptは、渡されたものに基づいて一意に名前が付けられた実装を適切に選択する「メタ関数」を実際に生成する必要があります。コンパイラーを渡すことができるリフトがありますが、型スニッフィングの実装が正しくない可能性があります。
エゼキエルビクター

5
@EzekielVictor TypeScriptは、実行時に型を確認する信頼できる方法があればそれを行います。
ソーン̈2016年

3
これはさらに複雑で、JavaScriptの型typeで実行できますが、インターフェイス、s、列挙型、ジェネリックスなどのTS固有の概念は実行時に失われます。それがあなたができない理由でもありますsomeObject instanceof ISomeInterfaceDefinedInTypeScript
モーガントゥーブレイクイリング2017

209

TypeScriptでオーバーロードすると、複数のシグネチャを持つ実装が1つだけになります。

class Foo {
    myMethod(a: string);
    myMethod(a: number);
    myMethod(a: number, b: string);
    myMethod(a: any, b?: string) {
        alert(a.toString());
    }
}

TypeScriptでは、実際の実装ではなく、メソッド呼び出しのシグネチャとして認識されるのは3つのオーバーロードのみです。

あなたの場合、パラメーターに十分な共通性がないため、個人的には名前の異なる2つのメソッドを使用します。そのため、メソッド本体は、何をするかを決定するために多くの「if」を必要とする可能性があります。

TypeScript 1.4

TypeScript 1.4以降、通常、共用体型を使用してオーバーロードの必要性を取り除くことができます。上記の例は、以下を使用してよりよく表現できます。

myMethod(a: string | number, b?: string) {
    alert(a.toString());
}

のタイプaは「どちらかstringまたはnumber」です。


すばらしい答えです。それを強調したいのですが、これは、次のような理由でオーバーロードしようとしているときに役に立たない可能性があります。 1インスタンスは、個々のparamsを渡す: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS

お電話の場合は、分岐する必要はありません-一般的に、私はむしろ、オブジェクトにそれぞれの道を私を作成するファクトリメソッドを使用したいFoo.fromObject(obj)Foo.fromJson(str)のように。
フェントン

しかし、それは常にパラメーターをオブジェクトまたは単一の文字列のいずれかとして渡すことを前提としています。前のコメントから強調表示されているように、それらを個別に渡したい場合はどうなりますか? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) また、FooクラスにはfromObjectとfromJson という異なるメソッドがあることに気づきましたか?
Hlawuleka MAS 2018

1
その違いをソースに戻すと、通常はその必要がないことがわかります。たとえば、とにかくタイプするmyNum必要myObjがあるので、個別のメソッドを用意してすべてを明確にし、不要な分岐ロジックを回避してください。
フェントン

2
パラメータに基づいて異なる戻り値の型が必要な場合は、共用体型を使用すると問題が発生する可能性があることに注意してください。戻り値の型が常にパラメーターの型の1つと一致する場合は、ジェネリックで解決できますが、それ以外の場合は、オーバーロードが最善の解決策です。
ジョンモンゴメリー

45

あなたはでき宣言し、複数の呼び出しシグネチャを持つ型を持つものとして関数を宣言することでオーバーロード機能を:

interface IFoo
{
    bar: {
        (s: string): number;
        (n: number): string;
    }
}

次に、次のようにします。

var foo1: IFoo = ...;

var n: number = foo1.bar('baz');     // OK
var s: string = foo1.bar(123);       // OK
var a: number[] = foo1.bar([1,2,3]); // ERROR

関数の実際の定義は単数であり、引数に対して内部で適切なディスパッチを実行する必要があります。

たとえば、クラスを使用します(これはを実装できますがIFoo、必須ではありません)。

class Foo
{
    public bar(s: string): number;
    public bar(n: number): string;
    public bar(arg: any): any 
    {
        if (typeof(arg) === 'number')
            return arg.toString();
        if (typeof(arg) === 'string')
            return arg.length;
    }
}

ここで興味深いのは、より具体的に型指定されたオーバーライドによってanyフォームが隠されていることです。

var foo2: new Foo();

var n: number = foo2.bar('baz');     // OK
var s: string = foo2.bar(123);       // OK
var a: number[] = foo2.bar([1,2,3]); // ERROR

1

一般に関数のオーバーロードとは何ですか?

関数のオーバーロードやメソッドのオーバーロードを作成する機能である複数の関数同じ名前を持つ異なる実装をウィキペディア


JSでの関数のオーバーロードとは何ですか?

JSではこの機能は使用できません。複数の宣言がある場合、最後に定義された関数が使用されます。

function foo(a1, a2) { return `${a1}, ${a2}` }
function foo(a1) { return `${a1}` } // replaces above `foo` declaration
foo(42, "foo") // "42"

...そしてTSでは?

オーバーロードは、JSランタイムに影響を与えないコンパイル時の構造です。

function foo(s: string): string // overload #1 of foo
function foo(s: string, n: number): number // overload #2 of foo
function foo(s: string, n?: number): string | number {/* ... */} // foo implementation

上記のコード(JSより安全)を使用すると、重複した実装エラーがトリガーされます。TSは最初のフィッティングオーバーロードをトップダウンの順序で選択するため、オーバーロードは最も固有のものから最も広いものへと並べ替えられます。


TSでのメソッドのオーバーロード:より複雑な例

オーバーロードされたクラスメソッドタイプは、関数のオーバーロードと同様の方法で使用できます。

class LayerFactory {
    createFeatureLayer(a1: string, a2: number): string
    createFeatureLayer(a1: number, a2: boolean, a3: string): number
    createFeatureLayer(a1: string | number, a2: number | boolean, a3?: string)
        : number | string { /*... your implementation*/ }
}

const fact = new LayerFactory()
fact.createFeatureLayer("foo", 42) // string
fact.createFeatureLayer(3, true, "bar") // number

関数の実装は、コンパイラーによって適用されるすべてのオーバーロードシグネチャと互換性があるため、非常に異なるオーバーロードが可能です。

詳細:


0

他の人に目を向けると、少なくともAngular 2のWebPackによってコンパイルされたTypeScriptによって明示されるように、overLOADEDメソッドの代わりに静かにoverWRITTENを取得することを観察しました。

myComponent {
  method(): { console.info("no args"); },
  method(arg): { console.info("with arg"); }
}

呼び出し:

myComponent.method()

引数なしでメソッドを実行し、引数なしのバージョンを無視して出力しているようです:

with arg

2
オーバーロードに対して別々のボディを宣言することはできません。異なるシグネチャのみを宣言してください。
adharris 2016年

5
使用しているTypeScriptコンパイラのバージョンはわかりませんが、現在のバージョンでは、Duplicate function implementationこのようなコードに対して警告が表示されます。
Royston Shufflebotham 2017年

0

typescriptでの関数のオーバーロード:

Wikipedia(および多くのプログラミングブック)によると、メソッド/関数のオーバーロードの定義は次のとおりです。

一部のプログラミング言語では、関数のオーバーロードまたはメソッドのオーバーロードは、異なる実装で同じ名前の複数の関数を作成する機能です。オーバーロードされた関数の呼び出しは、呼び出しのコンテキストに適したその関数の特定の実装を実行し、1つの関数呼び出しがコンテキストに応じて異なるタスクを実行できるようにします。

typescriptでは、引数の数と型に応じて呼び出される同じ関数の異なる実装を持つことはできません。これは、TSがJSにコンパイルされると、JSの関数には次の特性があるためです。

  • JavaScript関数定義は、パラメーターのデータ型を指定しません
  • JavaScript関数は、呼び出されたときに引数の数をチェックしません

したがって、厳密な意味では、TS関数のオーバーロードは存在しないと言えます。ただし、関数のオーバーロードを完全に模倣できるTSコード内で実行できることがいくつかあります。

次に例を示します。

function add(a: number, b: number, c: number): number;
function add(a: number, b: number): any;
function add(a: string, b: string): any;

function add(a: any, b: any, c?: any): any {
  if (c) {
    return a + c;
  }
  if (typeof a === 'string') {
    return `a is ${a}, b is ${b}`;
  } else {
    return a + b;
  }
}

TSドキュメントはこのメソッドをオーバーロードと呼び、基本的には、TSコンパイラに複数のメソッドシグネチャ(可能なパラメータと型の説明)を提供しています。これで、コンパイル時に関数を正しく呼び出したかどうかをTSが判断し、関数を誤って呼び出した場合にエラーを返すことができます。

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