261

誰かがTypeScriptでコンストラクタオーバーロードを行っていますか?言語仕様(v 0.8)の64ページには、コンストラクターのオーバーロードを記述したステートメントがありますが、サンプルコードはありません。

私は今、本当に基本的なクラス宣言を試しています。それはこのように見えますが、

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor(obj: IBox) {    
        this.x = obj.x;
        this.y = obj.y;
        this.height = obj.height;
        this.width = obj.width;
    }   

    constructor() {
        this.x = 0;
        this.y = 0;
        this.width = 0;
        this.height = 0;
    }
}

tsc BoxSample.tsを実行すると、重複したコンストラクタ定義がスローされます。これは明らかです。どんな助けもありがとうございます。


  • 私が知る限り、それはまだ複数のコンストラクタをサポートしていません - Johan

10 답변


222

TypeScriptでは、オーバーロードを宣言することができますが、実装は1つしかできず、実装にはすべてのオーバーロードと互換性のある署名が必要です。あなたの例では、これはinのようなオプションのパラメータで簡単に行うことができます。

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor(obj?: IBox) {    
        this.x = obj && obj.x || 0
        this.y = obj && obj.y || 0
        this.height = obj && obj.height || 0
        this.width = obj && obj.width || 0;
    }   
}

またはより一般的なコンストラクタを使用した2つのオーバーロード、

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor();
    constructor(obj: IBox); 
    constructor(obj?: any) {    
        this.x = obj && obj.x || 0
        this.y = obj && obj.y || 0
        this.height = obj && obj.height || 0
        this.width = obj && obj.width || 0;
    }   
}


  • 実際には、実行時に過負荷が発生したかどうかを判断するためにコンパイラにjavascriptを生成させることは可能です。しかし、彼らの哲学は可能な限り小さなJavaScriptを生成するように見えるので、これは起こりそうもありません。 - remcoder
  • @remcoder、それは常に真実です。いくつかの種類の情報は、実行時には利用できません。例えば、インタフェースの概念はありませんIBox生成されたJavaScriptでそれはクラスのために働くことができ、タイプに組み込まれましたが、私はこれを回避する潜在的な混乱が省略されたと仮定します。 - Drew Noakes
  • 別の非常に重要なメモ:TypeScriptはすでに型保証されていませんが、これはさらに侵入します。ここで行われたような関数のオーバーロードは、関数についてチェックできるプロパティを失います。コンパイラはもう注意を払わず、返された型が正しいものとみなします。 - froginvasion
  • これはタイプセーフではないのですか?そのタイプがまだ確実であることを確認していますnumber〜とpublic x: number。パラメータが渡された場合、正しいタイプのものであることを確認するという点で、安全性が重要です。 - nikk wong
  • @nikkwong froginvasionのポイントは、このテクニックを使用すると、オーバーロードに関するオーバーロードされた実装の正しさをタイプ・スクリプトが検証していないことです。コールサイトは検証されますが、実装は実装されていません。浮動小数点型の暗黙の定義を使用している "typesafe"ではなく、オーバーロードされた実装に型エラーの原因となるコードを制限します。 - chuckj

72

TypeScriptの既定のパラメータを使用して、実装レベルでのオーバーロードの不足を回避することもできます(例:

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor(obj : IBox = {x:0,y:0, height:0, width:0}) {    
        this.x = obj.x;
        this.y = obj.y;
        this.height = obj.height;
        this.width = obj.width;
    }   
}

編集:12月5日16分現在、ベンソンの答えより柔軟なソリューションを提供します。


65

についてコンストラクタのオーバーロード1つの代替案は、追加の過負荷を静的ファクトリメソッド。私は、あなたの呼び出し引数をテストするよりも読みやすく、あまり混乱しないと思います。ここに簡単な例があります:

class Person {
    static fromData(data: PersonData) {
        let { first, last, birthday, gender = 'M' } = data 
        return new this(
            `${last}, ${first}`,
            calculateAge(birthday),
            gender
        )
    }

    constructor(
        public fullName: string,
        public age: number,
        public gender: 'M' | 'F'
    ) {}
}

interface PersonData {
    first: string
    last: string
    birthday: string
    gender?: 'M' | 'F'
}


let personA = new Person('Doe, John', 31, 'M')
let personB = Person.fromData({
    first: 'John',
    last: 'Doe',
    birthday: '10-09-1986'
})

TypeScriptでのメソッドのオーバーロード本当のためではありませんコンパイラによって生成されたコードが多すぎるため、コアチームがそれを回避しようとするので、言いましょう。現在、メソッドオーバーロードが言語上に存在する主な理由は、APIにマジック引数を持つライブラリの宣言を記述する方法を提供することです。さまざまな議論を扱うためには、自分で重労働をする必要があるので、私は分離された方法の代わりに過負荷を使用することにあまり効果がありません。


53

注:これは、TypeScript 2.1を反映するために簡略化され、2011年4月13日に更新されました。TypeScript 1.8の解答の履歴を参照してください。

これは、オブジェクトパラメータをオプションにし、オブジェクトの各プロパティをオプションにしたいと思うようです。この例では、提供されているように、オーバーロード構文は必要ありません。私はいくつかの答えにいくつか悪い習慣を指摘したかったのです。確かに、それは本質的に書くことの可能な最小の表現ではないbox = { x: 0, y: 87, width: 4, height: 0 }これは、説明されているように、あなたがクラスから望むことができるすべてのコードヒントを提供します。この例では、1つ、いくつか、すべて、すべての関数を呼び出すことができます。またはいずれのパラメータも使用せず、デフォルト値を取得します。

 /** @class */
 class Box {
     public x?: number;
     public y?: number;
     public height?: number;
     public width?: number;     

     // The class can work double-duty as the interface here since they are identical
     // Alternately, reference your own interface, e.g.:  `...BoxI = {} as BoxI` 
     constructor(obj: Box = {} as Box) {

         // Define the properties of the incoming `obj` object here. 
         // Setting a default value with the `= 0` syntax is optional for each parameter
         let {
             x = 0,
             y = 0,
             height = 1,
             width = 1
         } = obj;

         //  If needed, make the parameters publicly accessible
         //  on the class ex.: 'this.var = var'.
         /**  Use jsdoc comments here for inline ide auto-documentation */
         this.x = x;
         this.y = y;
         this.height = height;
         this.width = width;
     }
 }

これは、定義されたオブジェクトのすべてのプロパティを持たない可能性のあるパラメータを書くための非常に安全な方法です。これで、次のいずれかを安全に書くことができます。

const box1 = new Box();
const box2 = new Box({});
const box3 = new Box({x:0});
const box4 = new Box({x:0, height:10});
const box5 = new Box({x:0, y:87,width:4,height:0});

 // Correctly reports error in TypeScript, and in js, box6.z is undefined
const box6 = new Box({z:0});  

コンパイルすると、オプションのパラメータは本当にオプションであることがわかります。これは、広く使用されている(しかしエラーが発生しやすい)フォールバック構文の落とし穴を回避します。var = isOptional || default;チェックしてvoid 0これは、undefined

コンパイルされた出力

var Box = (function () {
    function Box(obj) {
        if (obj === void 0) { obj = {}; }
        var _a = obj.x, x = _a === void 0 ? 0 : _a, _b = obj.y, y = _b === void 0 ? 0 : _b, _c = obj.height, height = _c === void 0 ? 1 : _c, _d = obj.width, width = _d === void 0 ? 1 : _d;
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
    }
    return Box;
}());

補遺:デフォルト値の設定:間違った方法

||(または)演算子

の危険を考えて||/または他の回答に示されているようにデフォルトのフォールバック値を設定するときは演算子を使用します。以下のコードは、デフォルトを設定する間違った方法を示しています。評価時に予期しない結果を得ることができます虚偽0、 ''、null、undefined、false、NaNのような値:

var myDesiredValue = 0;
var result = myDesiredValue || 2;

// This test will correctly report a problem with this setup.
console.assert(myDesiredValue === result && result === 0, 'Result should equal myDesiredValue. ' + myDesiredValue + ' does not equal ' + result);

Object.assign(this、obj)

私のテストでは、es6 / typescriptのdestructuredオブジェクトObject.assignよりもほぼ90%高速です。 destructuredパラメータを使用すると、オブジェクトに割り当てたメソッドとプロパティのみが許可されます。たとえば、次の方法を考えてみましょう。

class BoxTest {
    public x?: number = 1;

    constructor(obj: BoxTest = {} as BoxTest) {
        Object.assign(this, obj);
    }
}

別のユーザーがTypeScriptを使用しておらず、属していないパラメータを配置しようとした場合、たとえば、zプロパティ

var box = new BoxTest({x: 0, y: 87, width: 4, height: 0, z: 7});

// This test will correctly report an error with this setup. `z` was defined even though `z` is not an allowed property of obj.
console.assert(typeof box.z === 'undefined')


  • 私はこれが古いスレッドのようなものだと知っていますが、Iboxのキャストは私の心を壊しました。 - Nickso
  • Typescript 1.8のコーディングからのキャリーオーバーだった余分なキャストを削除するための私の答えを更新しました。残っているキャストは、パラメータが定義されていない場合は空のオブジェクト({}はデフォルトのオブジェクトになります。{}Boxとして検証しないと、Boxとしてキャストします。このようにキャストすると、パラメータが定義されていない新しいBoxを作成することができます。あなたのIDEでは、私の例だけでなく、const box1 = new Box();キャスティングが使用シナリオで表示されるエラーメッセージのいくつかをどのように解決するかを見ることができます。 - Benson
  • @Benson BoxTestの例にはエラーがあります。 TypeScriptコンパイラは、コンストラクタの間違った使用法について正しく苦情を言いますが、割り当ては引き続き発生します。アサーションは失敗しますbox.z 実際には7あなたのコードではなく、undefined。 - Steven Liekens
  • Boxクラスにメソッドを追加した後、コンストラクタが動作を停止します(コンパイル時に失敗しました)。何か案が? - JeeShen Lee
  • @ JeeShenLeeメソッドを使用して新しく名前を付けたクラスにBoxクラスを拡張するか、期待されるパラメータのインターフェイスを作成することができます。クラスはインターフェイスとして動作できるので、インターフェイスタイプはBoxクラスから借用されます。あなたの追加されたメソッドでは、クラスはオブジェクトの一部として渡されるメソッドを期待していたので、クラスはインターフェイスとして二重の役割を果たしています。 Boxクラスの最初の5行をコピーし、それを新しい名前のインターフェイスに変更します。interface BoxConfig { x?: number ...}行を変更しますconstructor(obj: BoxConfig = {} as BoxConfig) { - Benson

33

私はこれが古い質問であることを知っていますが、1.4の新機能は共用体型です。すべての関数オーバーロード(コンストラクターを含む)にこれらを使用します。例:

class foo {
    private _name: any;
    constructor(name: string | number) {
        this._name = name;
    }
}
var f1 = new foo("bar");
var f2 = new foo(1);


  • あなたはnameフィールドもタイプであるstring | numberの代わりにany? - Carcigenicate
  • あなたは間違いなくこれを行うことができます。もう少し一貫しているかもしれませんが、この例では、.toString()そして.valueOf()、Intellisenseでは、私のために、any彼/彼女自身にはうまくいっています。 - Joe

20

更新(2017年6月8日):guyaradとsnolflakeは私の答えに以下のコメントに有効な点を作っています。私は読者に答えを見ることを勧めますベンソンジョーそしてスノーフレーク誰が私よりも良い答えを持っています。

元の回答(2014年1月27日)

コンストラクタのオーバーロードを実現する方法の別の例:

class DateHour {

  private date: Date;
  private relativeHour: number;

  constructor(year: number, month: number, day: number, relativeHour: number);
  constructor(date: Date, relativeHour: number);
  constructor(dateOrYear: any, monthOrRelativeHour: number, day?: number, relativeHour?: number) {
    if (typeof dateOrYear === "number") {
      this.date = new Date(dateOrYear, monthOrRelativeHour, day);
      this.relativeHour = relativeHour;
    } else {
      var date = <Date> dateOrYear;
      this.date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
      this.relativeHour = monthOrRelativeHour;
    }
  }
}

ソース:http://mimosite.com/blog/post/2013/04/08/Overloading-in-TypeScript


  • これは建設的なコメントではありませんが、うわー、これは醜いです。何かのポイントを逃すタイプTypeScriptの安全性... - guyarad
  • それはコンストラクタのオーバーロードですか?結構です!そのクラスの静的ファクトリメソッドを実際は実装していますが、実際は実際は醜いです。 - cvsguimaraes
  • 今日私たちはdateOrYearを持つことができたと思います。数、 - Pascal Ganaye

3

オプションの型付きパラメータが十分である場合は、プロパティを繰り返したり、インタフェースを定義したりせずに同じ結果を達成する次のコードを検討してください。

export class Track {
   public title: string;
   public artist: string;
   public lyrics: string;

   constructor(track?: Track) {
     Object.assign(this, track);
   }
}

渡されたすべてのプロパティを割り当てますtrack、それらが定義されていない場合はeveTrack


1

@ ShinNoNoirのコードのような別のバージョンでは、デフォルト値とスプレッド構文を使用しています:

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor({x, y, height, width}: IBox = { x: 0, y: 0, height: 0, width: 0 }) {
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
    }
}


  • その場合は、パラメータを次のように入力する方が良いでしょう。{}の代わりにIBox?プロパティの制約を既に列挙しています... - Roy Tinker

0

あなたはこれを次のように処理できます:

import { assign } from 'lodash'; // if you don't have lodash use Object.assign
class Box {
    x: number;
    y: number;
    height: number;
    width: number;
    constructor(obj: Partial<Box> = {}) {    
         assign(this, obj);
    }
}

Partialはフィールド(x、y、height、width)オプションを作成し、複数のコンストラクタ

例:あなたができることnew Box({x,y})高さと幅なしで。

= {}未定義、nullなどの偽の値を処理しますnew Box()


-4

あなたはそのことを念頭に置いておくべきです...

contructor()

constructor(a:any, b:any, c:any)

それはnew()またはnew("a","b","c")

従って

constructor(a?:any, b?:any, c?:any)

上記と同じで、柔軟性があります...

new()またはnew("a")またはnew("a","b")またはnew("a","b","c")

リンクされた質問


最近の質問