클래스의 기본 형식은 다음과 같다.
class myClass{
consturctor() {
}
}
클래스를 정의하는 class
키워드를 사용했다. constructor
생성자는 클래스가 객체로 생성될 때 기본적으로 호출되는 메소드이다.
TS를 JS로 변환하면 다음과 같다.
//TS
class class_name { //class scope }
//JS
var class_name = (function () {
function class_name() {
}
return class_name;
}());
클래스 내부 구조를 좀 더 살펴보면 다음과 같은 요소가 있다.
var
키워드를 사용하지 않는다.this
키워드는 class의 현재 instance를 가리킨다. 파라미터 이름은 클래스의 필드 이름과 같다. 클래스의 필드는 this
키워드를 붙여서 인자값과 구분해준다.function
키워드를 사용하지 않는다.TS로 car라는 class를 선언한다.
//TS
class Car {
//field
engine:string;
//constructor
constructor(engine:string) {
this.engine = engine
}
//function
disp():void {
console.log("Engine is : "+this.engine)
}
}
이를 JS로 바꾸면 다음과 같다.
//JS
var Car = (function () {
//constructor
function Car(engine) {
this.engine = engine;
}
//function
Car.prototype.disp = function () {
console.log("Engine is : " + this.engine);
};
return Car;
}());
필드를 전역 변수로 선언할 때 구분을 위해 필드 앞에 _
를 붙이기도 한다. 곧바로 생성자 매개변수를 전역화하고 싶다면 매개변수에 선언한 변수 앞에 public
접근자를 추가한다. public
접근자를 이용해 생성자의 매개변수가 아닌 클래스의 전역변수로 변수의 유효 범위가 확장된다.
class MyCar
constructor(public carName: string, public _numTier){}
// 메소드
getCarName(): string{
return this.carName;
}
// 게터 함수
get numTier(){ return this._numTier;}
}
클래스는 그 자체로 사용할 수 없으며 new
키워드를 통해 인스턴스를 생성하여 사용해야 한다. 표현식의 오른쪽 구문을 통해서 생성자를 호출한다. 객체에 할당하기 전에 new
키워드로 초기화를 하며 이때 생성자에 인자가 있다면 값을 전달해준다.
var myClass:MyClass = new MyClass();
클래스를 선언할 때는 선언자와 함께 객체를 담을 변수를 선언한다. 생략해도 무관하나 명시적인 타입을 통해 변수를 정의할 수도 있다.
var myClass:MyClass;
myClass = new MyClass();
선언과 객체 생성을 분리하는 것도 가능하다.
var object_name = new class_name([argument])
object_name.field_name
object_name.function_name()
객체이름과.
으로 구분하여 class의 필드나 메소드에 접근한다.
Car 라는 Class를 할당 받은 객체 obj의 동작을 살펴본다.
class Car {
//field
engine:string;
//constructor
constructor(engine:string) {
this.engine = engine
}
//function
disp():void {
console.log("Function displays Engine is : "+this.engine)
}
}
//create an object
var obj = new Car("XXSY1")
//access the field
console.log("Reading attribute value Engine as : "+obj.engine)
//access the function
obj.disp()
결과
Reading attribute value Engine as : XXSY1
Function displays Engine is : XXSY1
JS 로 변환
var Car = (function () {
//constructor
function Car(engine) {
this.engine = engine;
}
//function
Car.prototype.disp = function () {
console.log("Function displays Engine is : " + this.engine);
};
return Car;
}());
//create an object
var obj = new Car("XXSY1");
//access the field
console.log("Reading attribute value Engine as : " + obj.engine);
//access the function
obj.disp();
class child_class_name extends parent_class_name
extend
키워드를 통해 지정한 부모 클래스로부터 속성과 메소드를 상속 받는다. (JS에서는 prototype
이라는 기능을 사용해야 했지만 ES6부터 클래스 개념이 추가되었다.)static
키워드를 통해 프로그램이 종료될 때까지 값을 유지할 수 있다. static
으로 선언된 속성이나 메소드는 [class_name] + .
+ [member_name]으로 참조된다.instaceof
연산자를 통해 객체가 지정된 Type에 속한다면 true를 반환한다. (참고: 자바스크립트의 Type Checking)//TS
class Animal{
protected constructor(public name: string, public leg: number){}
getLeg(): number{
return this.leg;
}
protected getName(): string{
return this.name;
}
}
class Monkey{
constructorb(name: string, leg: number){
super(name, leg);
}
isClimbing(){
return true;
}
superGetName(){
return super.getName;
}
}
var monkey: Monkey=new Monkey("Lemur", 2)
console.log("원숭이 이름: "+monkey.name);
console.log("나무타기 가능 여부 : "+monkey.isClimbing);
console.log("다리 개수: "+monkey.getLeg());
console.log("상위 클래스의 메소드 호출 : "+monkey.superGetName);
결과
원숭이 이름 : Lemur
나무타기 가능 여부 : True
다리 개수 : 2
상위 클래스의 메소드 호출 : Lemur
class PrinterClass {
doPrint():void {
console.log("doPrint() from Parent called…")
}
}
class StringPrinter extends PrinterClass {
doPrint():void {
super.doPrint()
console.log("doPrint() is printing a string…")
}
}
var obj = new StringPrinter()
obj.doPrint()
super
키워드를 통해 부모클래스의 변수, 속성, 메소드를 가리킨다.결과
doPrint() from Parent called…
doPrint() is printing a string…
JS로 컴파일하면 다음과 같다.
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var PrinterClass = (function () {
function PrinterClass() {
}
PrinterClass.prototype.doPrint = function () {
console.log("doPrint() from Parent called…");
};
return PrinterClass;
}());
var StringPrinter = (function (_super) {
__extends(StringPrinter, _super);
function StringPrinter() {
_super.apply(this, arguments);
}
StringPrinter.prototype.doPrint = function () {
_super.prototype.doPrint.call(this);
console.log("doPrint() is printing a string…");
};
return StringPrinter;
}(PrinterClass));
var obj = new StringPrinter();
obj.doPrint();
TS에 의해 지원되는 접근 수정자(접근 지정자)는 3개이다.
TypeScript - Interface | PoiemaWeb
?
키워드를 붙여 선택적 프로퍼티로 선언할 수 있다.)abstract
키워드를 명시하지는 않는다.extend
키워드를 사용해 인터페이스 또는 클래스를 상속 받을 수 있다. 인터페이스는 여러개를 상속 받을 수 있다. (클래스는 안 된다.) 클래스를 상속 받을 때 모든 접근 지시자의 멤버가 상속되지만 구현은 상속되지 않는다.type
이 좀 더 구조체에 가까운 느낌이다. 에러가 발생했을 때 인터페이스는 인터페이스 이름을 보여주지만 type은 UUID 표현식의 우항을 보여준다.(1) 변수에서 사용하기
// 인터페이스의 정의
interface Todo {
id: number;
content: string;
completed: boolean;
}
// 변수 todo의 타입으로 Todo 인터페이스를 선언하였다.
let todo: Todo;
// 변수 todo는 Todo 인터페이스를 준수하여야 한다.
todo = { id: 1, content: 'typescript', completed: false };
// 인터페이스의 정의
interface Todo {
id: number;
content: string;
completed: boolean;
}
let todos: Todo[] = [];
// 파라미터 todo의 타입으로 Todo 인터페이스를 선언하였다.
function addTodo(todo: Todo) {
todos = [...todos, todo];
}
// 파라미터 todo는 Todo 인터페이스를 준수하여야 한다.
const newTodo: Todo = { id: 1, content: 'typescript', completed: false };
addTodo(newTodo);
console.log(todos)
// [ { id: 1, content: 'typescript', completed: false } ]
(2) 함수로 사용하기
// 함수 인터페이스의 정의
interface SquareFunc {
(num: number): number;
}
// 함수 인테페이스를 구현하는 함수는 인터페이스를 준수하여야 한다.
const squareFunc: SquareFunc = function (num: number) {
return num * num;
}
console.log(squareFunc(10)); // 100
(3) 클래스로 사용하기
// 인터페이스의 정의
interface ITodo {
id: number;
content: string;
completed: boolean;
}
// Todo 클래스는 ITodo 인터페이스를 구현하여야 한다.
class Todo implements ITodo {
constructor (
public id: number,
public content: string,
public completed: boolean
) { }
}
const todo = new Todo(1, 'Typescript', false);
console.log(todo);
implements
키워드를 사용해서 인터페이스를 가져오고 있는 클래스는 반드시 해당 인터페이스를 준수해서 구현해야 한다.// 인터페이스의 정의
interface IPerson {
name: string;
sayHello(): void;
}
/*
인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메소드를 반드시 구현하여야 한다.
*/
class Person implements IPerson {
// 인터페이스에서 정의한 프로퍼티의 구현
constructor(public name: string) {}
// 인터페이스에서 정의한 추상 메소드의 구현
sayHello() {
console.log(`Hello ${this.name}`);
}
}
function greeter(person: IPerson): void {
person.sayHello();
}
const me = new Person('Lee');
greeter(me); // Hello Lee
추상 클래스는 구현과 강제를 동시에 수행하는 클래스다. abstract
키워드를 이용해 선언하며, 추상 클래스 내부는 추상 메소드와 구현 메소드로 나누어 정의한다.
추상 메소드에도 동일하게 abstract
키워드를 붙이며 추상 클래스 안에서 선언만 되고, 실제 구현은 추상 클래스를 상속 받은 클래스 안에서 이루어진다.
abstract class SmallAnimals {
abstract sound(): string;
abstract name(): string;
makeSound(): string{
// 추상메소드를 이용해 구현메소드의 로직을 추가
return `${this.name()} : ${this.sound()} `;
}
}
class Mouse extends SmallAnimals{
// 추상클래스에 정의된 추상메소드를 구현
// 구현한 메소드의 실제 구현 방식은 추상클래스 내부의 구현 메소드가 결정
sound(): string {
return "peep";
}
name(): string {
return "mouse";
}
// 클래스 고유의 메소드나 변수 추가
}
var mouse = new Mouse();
console.log(mouse.makeSound());
structural typing(구조적 타이핑)
혹은 duck typing(덕 타이핑)
이라고 한다.(1) 함수 예시
interface IDuck { // 1
quack(): void;
}
class MallardDuck implements IDuck { // 3
quack() {
console.log('Quack!');
}
}
class RedheadDuck { // 4
quack() {
console.log('q~uack!');
}
}
function makeNoise(duck: IDuck): void { // 2
duck.quack();
}
makeNoise(new MallardDuck()); // Quack!
makeNoise(new RedheadDuck()); // q~uack! // 5
implements
키워드를 통해 인터페이스를 구현하지 않았다.(2) 변수 예시
interface IPerson {
name: string;
}
function sayHello(person: IPerson): void {
console.log(`Hello ${person.name}`);
}
const me = { name: 'Lee', age: 18 };
sayHello(me); // Hello Lee
:
연산자를 통해 명시해야 한다.)function decorator_name(target){ // }
@decorator_name
function test(){}
@expression
로 표현되며, 이때 expression은 함수이며 런타임에 호출된다.tsconfig.json
에서 experimentalDecorotor
를 true로 선언해서 사용할 수 있다. (설정을 안 하면 다음 릴리즈에서 지원하지 않을 수 있다는 경고가 뜬다.)아래 예제는 클래스의 프로퍼티에 데코레이터를 작성한 것이다.
function fistDecorator(target, name) {
console.log('fistDecorator');
}
class Person {
@fistDecorator
job = 'programmer';
}
const p = new Person();
console.log(p.job);
// fistDecorator
// programmer
또다른 예시로 angular의 app.component.ts 파일에 있는 Component 데코레이터를 살펴보자.
// src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector:'app-root'
templeteUrl: './app/component.html',
styleUrls:['./app.component.css']
})
export class AppComponent{
title: 'my-project';
}
function decorator_factory(value: string){
return function (target) {
// target과 value 변수를 사용한 동작
}
}
function firstDecorator(param) {
console.log('factory’);
return function(target, name) {
console.log('decorator');
}
}
class SomeClass {
@firstDecorator(123)
prop = ‘a';
}
console.log('인스턴스가 만들어지기 전');
console.log(new SomeClass());
// factory
// decorator
// 인스턴스가 만들어지기 전
function decoA(param) {
console.log('decoA factory');
return function(target, name) {
console.log('decyA decorator')
}
}
function decoB(target, name) {
console.log('decoB decorator');
}
function decoC(param) {
console.log('decoC factory');
return function(target, name) {
console.log('decoC decorator');
}
}
class SomeClass {
@decoA(1)
@decoB
@decoC(2)
prop = 1;
}