523

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 で実行すると、重複したコンストラクター定義がスローされます。これは明らかです。どんな助けでも大歓迎です。

4

17 に答える 17

388

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

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 ?? 0
        this.y = obj?.y ?? 0
        this.height = obj?.height ?? 0
        this.width = 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?: IBox) {    
        this.x = obj?.x ?? 0
        this.y = obj?.y ?? 0
        this.height = obj?.height ?? 0
        this.width = obj?.width ?? 0;
    }   
}

遊び場で見る

于 2012-10-03T06:14:22.700 に答える
197

コンストラクターのオーバーロードに関しては、追加のオーバーロードを静的ファクトリ メソッドとして実装することも 1 つの良い方法です。コンストラクターで可能なすべての引数の組み合わせをチェックするよりも読みやすく、簡単だと思います。

次の例では、異なる方法で値を保存する保険会社からのデータを使用して患者オブジェクトを作成できます。患者のインスタンス化のためにさらに別のデータ構造をサポートするには、別の静的メソッドを追加して、提供されたデータを正規化した後、デフォルトのコンストラクターを最善の方法で呼び出すことができます。

class Patient {
    static fromInsurance({
        first, middle = '', last,
        birthday, gender
    }: InsuranceCustomer): Patient {
        return new this(
            `${last}, ${first} ${middle}`.trim(),
            utils.age(birthday),
            gender
        );
    }

    constructor(
        public name: string,
        public age: number,
        public gender?: string
    ) {}
}

interface InsuranceCustomer {
    first: string,
    middle?: string,
    last: string,
    birthday: string,
    gender: 'M' | 'F'
}


const utils = { /* included in the playground link below */};

{// Two ways of creating a Patient instance
    const
        jane = new Patient('Doe, Jane', 21),
        alsoJane = Patient.fromInsurance({ 
            first: 'Jane', last: 'Doe',
            birthday: 'Jan 1, 2000', gender: 'F'
        })

    console.clear()
    console.log(jane)
    console.log(alsoJane)
}

出力はTS Playgroundで確認できます


TypeScript でのメソッドのオーバーロードは、実際にはそうではありません。たとえば、コンパイラで生成されたコードが多すぎるため、TS はそれを回避するように設計されています。メソッドのオーバーロードの主な使用例は、API に魔法の引数を持つライブラリの宣言を書くことでしょう。可能な引数のさまざまなセットを処理するという面倒な作業はすべてユーザーが行うため、シナリオごとにアドホック メソッドではなくオーバーロードを使用するメリットはあまりありません。

于 2016-07-31T21:08:19.627 に答える
50

更新 2 (2020 年 9 月 28 日):この言語は常に進化しているため、(v2.1 で導入された) を使用できる場合Partial、これはこれを達成するための私の推奨方法です。

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

   public constructor(b: Partial<Box> = {}) {
      Object.assign(this, b);
   }
}

// Example use
const a = new Box();
const b = new Box({x: 10, height: 99});
const c = new Box({foo: 10});          // Will fail to compile

更新 (2017 年 6 月 8 日): guyarad と snolflake は、私の回答に対する以下のコメントで有効な点を指摘しています。BensonJoe、および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

于 2014-01-27T16:02:44.593 に答える
7

あなたのBoxクラスは、複数のコンストラクターの実装を定義しようとしています。

クラスコンストラクターの実装として、最後のコンストラクターオーバーロード シグネチャのみが使用されます。

以下の例では、コンストラクターの実装が、前述のオーバーロード シグネチャのいずれとも矛盾しないように定義されていることに注意してください。

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

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

    constructor() /* Overload Signature */
    constructor(obj: IBox) /* Overload Signature */
    constructor(obj?: IBox) /* Implementation Constructor */ {
        if (obj) {
            this.x = obj.x;
            this.y = obj.y;
            this.width = obj.width;
            this.height = obj.height;
        } else {
            this.x = 0;
            this.y = 0;
            this.width = 0;
            this.height = 0
        }
    }

    get frame(): string {
        console.log(this.x, this.y, this.width, this.height);
    }
}

new Box().frame; // 0 0 0 0
new Box({ x:10, y:10, width: 70, height: 120 }).frame; // 10 10 70 120



// You could also write the Box class like so;
class Box {
    public x: number = 0;
    public y: number = 0;
    public width: number = 0;
    public height: number = 0;

    constructor() /* Overload Signature */
    constructor(obj: IBox) /* Overload Signature */
    constructor(obj?: IBox) /* Implementation Constructor */ {
        if (obj) {
            this.x = obj.x;
            this.y = obj.y;
            this.width = obj.width;
            this.height = obj.height;
        }
    }

    get frame(): string { ... }
}
于 2020-01-11T04:28:39.300 に答える
4

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

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

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

trackで定義されていない場合でも、渡されたすべてのプロパティが割り当てられることに注意してくださいTrack

于 2016-11-06T00:22:21.230 に答える
4
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) {
        const { x, y, height, width } = { x: 0, y: 0, height: 0, width: 0, ...obj }
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
    }
}
于 2016-11-30T05:11:40.190 に答える
1

@Bensonの回答でコメントされているように、コードでこの例を使用しましたが、非常に便利であることがわかりました。ただしObject is possibly 'undefined'.ts(2532)、クラス変数の型を使用して計算を試みたときにエラーが発生しました。疑問符が型になるためですAssignedType | undefined。未定義のケースが後の実行で処理された場合、またはコンパイラの型強制で処理された場合でも<AssignedType>、エラーを取り除くことができなかったため、引数をオプションにすることができませんでした。疑問符の params とクラス変数を使用して、引数の分離型を作成することを解決しました。疑問符なしで。冗長ですが、機能しました。

クラス method() でエラーが発生した元のコードは次のとおりです。以下を参照してください。

/** @class */

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

  // The Box class can work double-duty as the interface here since they are identical
  // If you choose to add methods or modify this class, you will need to
  // define and reference a new interface for the incoming parameters object 
  // e.g.:  `constructor(params: BoxObjI = {} as BoxObjI)` 
  constructor(params: Box = {} as Box) {
    // Define the properties of the incoming `params` object here. 
    // Setting a default value with the `= 0` syntax is optional for each parameter
    const {
      x = 0,
      y = 0,
      height = 1,
      width = 1,
    } = params;

    //  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;
  }

  method(): void {
    const total = this.x + 1; // ERROR. Object is possibly 'undefined'.ts(2532)
  }
}

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 });

そのため、変数はクラス メソッドで使用できません。たとえば、次のように修正すると、次のようになります。

method(): void {
    const total = <number> this.x + 1;
}

今、このエラーが表示されます:

Argument of type '{ x: number; y: number; width: number; height: number; }' is not 
assignable to parameter of type 'Box'.
Property 'method' is missing in type '{ x: number; y: number; width: number; height: 
number; }' but required in type 'Box'.ts(2345)

あたかも引数バンドル全体がオプションではなくなったかのように。

したがって、オプションの引数を持つ型が作成され、クラス変数がオプションから削除された場合、引数はオプションになり、クラスメソッドで使用できるようになります。ソリューション コードの下:

type BoxParams = {
  x?: number;
  y?: number;
  height?: number;
  width?: number;
}

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

  // The Box class can work double-duty as the interface here since they are identical
  // If you choose to add methods or modify this class, you will need to
  // define and reference a new interface for the incoming parameters object 
  // e.g.:  `constructor(params: BoxObjI = {} as BoxObjI)` 
  constructor(params: BoxParams = {} as BoxParams) {
    // Define the properties of the incoming `params` object here. 
    // Setting a default value with the `= 0` syntax is optional for each parameter
    const {
      x = 0,
      y = 0,
      height = 1,
      width = 1,
    } = params;

    //  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;
  }

  method(): void {
    const total = this.x + 1;
  }
}

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 });

時間をかけて読んで、私が言おうとしている点を理解しようとする人からのコメントを歓迎します.

前もって感謝します。

于 2020-10-20T19:37:00.267 に答える
1

次の代替手段を使用して、デフォルト/オプションのパラメーターと、可変数のパラメーターを持つ「オーバーロードされた」コンストラクターを取得します。

private x?: number;
private y?: number;

constructor({x = 10, y}: {x?: number, y?: number}) {
 this.x = x;
 this.y = y;
}

私はそれがこれまでで最もきれいなコードではないことを知っていますが、それに慣れます. 追加のインターフェイスは必要なく、プライベート メンバーを使用できますが、これはインターフェイスを使用する場合には不可能です。

于 2019-09-20T11:28:12.817 に答える
1

これは実用的な例であり、より多くのフィールドを持つすべてのコンストラクターは、余分なフィールドを としてマークする必要があることを考慮する必要がありますoptional

class LocalError {
  message?: string;
  status?: string;
  details?: Map<string, string>;

  constructor(message: string);
  constructor(message?: string, status?: string);
  constructor(message?: string, status?: string, details?: Map<string, string>) {
    this.message = message;
    this.status = status;
    this.details = details;
  }
}
于 2020-03-04T08:41:45.517 に答える