이 글은 !(Typescript Class: Extending-classes-and-interfaces-using-typescript)[https://www.pluralsight.com/blog/software-development/extending-classes-and-interfaces-using-typescript]를 번역한 글이고 오역, 의역 많습니다.. 글을 다 읽지 마시고 코드 보면서 이렇게 변하는 구나~ 하면서 이해해주세요.
인터페이스 그리고 클래스를 확장해봅시다.
다음 블로그 > declare typescript
다다음 블로그 > https://www.youtube.com/watch?v=Fgcu_iB2X04
class Auto {
private _basePrice: number;
engine: IEngine;
state: string;
make: string;
model: string;
year: number;
accessoryList: string;
constructor(options: IAutoOptions) {
this.engine = options.engine;
this.basePrice = options.basePrice;
this.state = options.state;
this.make = options.make;
this.model = options.model;
this.year = options.year;
}
calculateTotal() : number {
var taxRate = TaxRateInfo.getTaxRate(this.state);
return this.basePrice + (taxRate.rate * this.basePrice);
}
addAccessories(...accessories: Accessory[]) {
this.accessoryList = '';
for (var i = 0; i < accessories.length; i++) {
var ac = accessories[i];
this.accessoryList += ac.accessoryNumber + ' ' + ac.title + '<br />';
}
}
getAccessoryList(): string {
return this.accessoryList;
}
get basePrice(): number {
return this._basePrice;
}
set basePrice(value: number) {
if (value <= 0) throw 'price must be >= 0';
this._basePrice = value;
}
}
코드를 보시면 다양한 필드, 생성자, 함수(스프레드 매개변수같은 스페셜 타입을 포함한 함수) 그리고 basePrice로 이름지어진 프로퍼티 get, set 을 볼 수 있습니다.
비록 상속과는 관계가 없지만, --target 스위치를 사용하여 TypeScript 컴파일 대상을 ECMAScript 5로 설정할 때만 TypeScript의 속성이 작동한다는 점에 유의해야 합니다. (예: tsc.ext --target ES5 YourFile.ts)
interface IEngine {
start(callback: (startStatus: bool, engineType: string) => void) : void;
stop(callback: (stopStatus: bool, engineType: string) => void) : void;
}
interface IAutoOptions {
engine: IEngine;
basePrice: number;
state: string;
make: string;
model: string;
year: number;
}
IEngine 인터페이스는 start() 그리고 stop() 메소드를 둘 다 콜백함수로 허용합니다.
콜백 함수는 타입이 boolean, string 인 두 개의 매개 변수를 필수로 받아야 합니다.
implement를 사용한 인터페이스 예제를 보여드리겠습니다.
인터페이스를 implement 받는 클래스는 인터페이스의 ? 연산자로 정의되지 않은 모든 멤버를 반드시 정의해야 합니다.
class Engine implements IEngine {
constructor(public horsePower: number, public enginType: string) { }
start(callback: (startStatus: bool, engineType: string) => void): vod {
window.setTimeout(() => {
callback(true, this.engineType);
}, 1000);
}
stop(callback: (stopStatus: bool, engineType: string) => void): vod {
window.setTimeout(() => {
callback(true, this.engineType);
}, 1000);
}
}
Auto 클래스를 확장한 Truck 클래스가 있다고 했을 때 우리는 Auto 에 있는 코드들을 복사/붙여넣기 하기 싫습니다. 운 좋게도, 타입스크립트는 우리에게 상속 기능을 제공해줍니다. 아래의 코드가 Auto 클래스를 상속 받은 Truck 클래스 입니다.
class Truck extends Auto {
private _bedLength: string;
fourByFour: bool;
constructor(options: ITruckOptions) {
super(options);
this.bedLength = options.bedLength; // Auto 클래스의 멤버
this.fourByFour = options.fourByFour; // Auto 클래스의 맴버
}
get bedLength(): string {
return this._bedLength;
}
set bedLength(value: string) {
if (value == null || value == undefined || value == "") {
this._bedLength = "Short";
} else {
this._bedLength = value;
}
}
}
Truck 클래스는 bedLength
그리고 foutByFour
정의하면서 Auto 클래스를 확장하였습니다.
마찬가지로 Interface
도 extends 키워드를 사용함으로서 확장할 수 있습니다.
interface ITruckOptions extends IAutoOptions {
bedLength: string;
foutByFour: bool;
}
트럭 클래스는 bedLength
그리고 fourByFour
기능을 추가함으로서 오토 클래스를 확장했습니다.
인터페이스 또한 extends 키워드를 사용해서 상속을 받을 수 있습니다.
interface ITruckOptions extends IAutoOptions {
bedLength: string;
fourByFour: bool;
}
ITruckOptions 인터페이스에 맞게 오브젝트를 트럭 클래스 생성자의 매개변수로 주는 트럭 인스턴스 객체를 생성하는 예제입니다.
let truck = new Truck({
engine: new Engine(250, "V8"),
basePrice: 45000,
state: "Arizona",
make: "Ford",
model: "F-150",
year: 2013,
bedLength: "Short Bed",
fourByFour: true
});
타입스크립트의 extends 키워드가 간단하고 편리한 함수형 상속을 지원한다는 것을 볼 수 있습니다. 그러나 Javascript 가 컴파일 할 때 뒤에서 무슨 일이 일어날까요?
어쨋든, Javascript는 ECMAScript 5 또는 그 이전 버전에서는 extends 또는 inherits 키워드를 가지고 있지 않습니다.
만약 Typescript가 Javascript로 컴파일 한 코드를 본다면 Javascript 프로토타입을 사용한 상속한 척 하는 작은 마법을 보실 것입니다.
첫 번째로, 생성된 자바스크립트에 __extends
라는 변수가 추가됩니다. 그리고 아래와 같은 두 개의 매개변수를 가지는 함수가 할당됩니다.
var __extends = this.__extends || function (d, b) {
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
이 함수는 파생(derived)/자식 타입(d 파라미터)과 기본 타입(b 파라미터) 받습니다. 함수 안의 이름이 __
인 오브젝트가 만들어졌습니다. 그리고 생성자에서 __
에 파생(derived)타입을 할당합니다. 거기서, 기본 타입의 프로토타입에 오브젝트의 프로토타입을 할당합니다. 작업을 끝내려면 __
인스턴스 객체를 만들고 자식 타입의 프로토타입으로 할당됩니다. 그래서 새 인스턴스가 기본 타입의 멤버를 가져갑니다.
끝으로, 자바스크립트는 두 오브젝트 사이를 상속할 수 있도록 하는 재사용 함수를 생성합니다.
만약 프로토타입에 처음이시라면 아마도 타입스크립트 extends 키워드의 간단함의 진가를 알아보셨을 것입니다.
__extends
함수는 Javascript로 변환된 코드에서 Truck과 Auto 사이의 상속을 다루는 데에도 사용되었습니다.
다음 예제는 변환된 대표적인 Truck 클래스를 보여줍니다.
var Truck = (function (_super) {
__extends(Truck, _super);
function Truck(options) {
_super.call(this, options);
this.bedLength = options.bedLength;
this.fourByFour = options.fourByFour;
}
Object.defineProperty(Truck.prototype, "bedLength", {
get: function () {
return this._bedLength;
},
set: function (value) {
if (value == null || value == undefined || value == "") {
this._bedLength = "Short";
} else {
this._bedLength = value;
}
},
enumerable: true,
configurable: true
});
return Truck;
})(Auto);
Truck은 이름이 _super
인 매개변수를 받는 함수를 할당받습니다. 이 매개변수는 상속하고 싶은 부모 클래스를 대표합니다. Truck에 할당된 함수는 코드 하단에 자체 설치되며, _super
파라미터의 값에 대해 (예제의 Auto) 파생되는 기본 클래스가 전달된다. __extends
함수는 Truck 안의 호출에 의해 앞에서 논의되었다. 그리고 파생된 타입(Truck), 기본 타입(Auto)들은 파라미터로서 전달된다. 그러면 앞서 논의한 대로 prototypes을 사용하여 상속의 마법이 일어난다.
// 이 부분은 제 입맛대로 쓴 거에요..!
위의 예제를 통해 우리는 Typescript의 extedns는 컴파일러를 이용해 내부적으로 Javascript로 변환되면서 모든 브라우저에서 사용할 수 있게 하는 것을 알았다.
interface, implement 그리고 extend가 타입스크립트에서 어떻게 만들어지는 지를 알았다.