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를 사용하여 실행하면 중복 생성자 정의가 throw됩니다. 이는 명백합니다. 어떤 도움을 주셔서 감사합니다.


  • 지금까지 내가 말할 수있는 한, 그것은 아직 여러 생성자를 지원하지 않는다. - Johan

10 답변


222

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

또는보다 일반적인 생성자를 사용하는 두 개의 오버로드,

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를 생성 할 수 있어야합니다. 그러나 그들의 철학이 가능한 한 적은 자바 스크립트를 생성하는 것처럼 보이기 때문에 이것은 거의 일어나지 않을 것입니다. - remcoder
  • @remcoder는 항상 사실입니다. 일부 유형의 정보는 런타임에 사용할 수 없습니다. 예를 들어, 인터페이스 개념이 없습니다.IBox생성 된 JavaScript에서. 그것은 클래스를 위해 일할 수 있고 타입으로 만들었지 만, 이것 주위의 잠재적 인 혼동이 생략되었다고 가정합니다. - Drew Noakes
  • 매우 중요한 또 다른주의 사항 : TypeScript는 이미 타입 안전하지는 않지만, 이것은 더 나아가 침입합니다. 여기에서 수행 된 함수 오버로딩은 함수에 대해 확인할 수있는 모든 속성을 잃습니다. 컴파일러는 더 이상주의를 기울이지 않으며 리턴 된 타입이 올바른 것으로 가정합니다. - froginvasion
  • 무엇이 타입 안전하지 않은가? 유형을 계속해서 확인합니다.numberpublic x: number. 매개 변수가 전달 된 경우 올바른 유형인지 확인하기 위해 안전성이 중요합니다. - nikk wong
  • @nikkwong froginvasion의 포인트는이 기술을 사용하여 타입 스크립트가 오버로드에 대한 오버로드 된 구현의 정확성을 검증하지 않는다는 것입니다. 호출 사이트는 검증되었지만 구현은 그렇지 않습니다. 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

에 관해서생성자 과부하하나의 대안은 다음과 같이 추가 오버로드를 구현하는 것입니다.정적 팩토리 메소드. 필자는 호출 인수를 테스트하는 것보다 읽기 쉽고 덜 혼란 스럽다고 생각합니다. 다음은 간단한 예입니다.

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 }그러나 이것은 설명 된대로 클래스에서 원하는 모든 코드 힌트를 제공합니다. 이 예제에서는 하나, 일부, 모두,또는매개 변수가 없으며 여전히 기본값을 얻습니다.

 /** @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를 사용했다.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를 만들 수 있습니다. IDE에서 예제를 입력 할 수 있습니다.const box1 = new Box();캐스팅이 사용 시나리오에서 볼 수있는 오류 메시지 중 일부를 해결하는 방법을 확인할 수 있습니다. - Benson
  • @Benson BoxTest 예제에는 오류가 있습니다. TypeScript 컴파일러는 생성자의 잘못된 사용법에 대해 올바르게 불평하지만 할당은 계속 발생합니다. 어설 션은box.z ~이다.사실로7귀하의 코드에undefined. - Steven Liekens
  • Box 클래스에 메서드를 추가 한 다음 생성자가 작동을 멈췄습니다 (컴파일 타임에 실패 함). 어떤 생각? - JeeShen Lee
  • @ JeeShenLee 당신은 Box 클래스를 메서드를 가진 새로 명명 된 클래스로 확장하거나 예상되는 매개 변수에 대한 인터페이스를 만들 수 있습니다. 클래스는 인터페이스로 작동 할 수 있으므로 인터페이스 유형은 Box 클래스에서 빌려옵니다. 추가 된 메서드를 사용하면 클래스가 인터페이스로 이중 작업을 수행하기 때문에 인터페이스에서 메서드가 개체의 일부로 전달 될 것으로 예상했습니다. Box 클래스의 처음 다섯 줄을 복사하여 다음과 같이 새 이름을 가진 인터페이스로 변경하십시오.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, 정의되지 않은 경우 이브Track.


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")

연결된 질문


최근 질문