TypeScript Classes

100pearlcent·2021년 8월 29일
0

TypeScript

목록 보기
5/6
post-thumbnail

Class란❓

  • object를 만드는 청사진
  • 클래스 이전에 object를 만드는 기본적인 방법은 function
  • JavaScript에도 class는 ES6부터 사용 가능
  • OOP를 위한 초석
  • TypeScript에서는 클래스도 사용자가 만드는 타입 중 하나
class Person {
  	name;
  
	constructor(name: string) {
    	this.name = name; // this = 생성된 object 자신
    }
  
}

const p1 = new Person('Jinju');

console.log(p1); // Person { name: 'Jinju' }
  • class 이름은 보통 대문자로 시작
  • new 키워드로 class를 통해 object를 만들 수 있다
  • constructor를 이용해서 object를 생성하면서 값을 전달할 수 있다
  • this를 이용해서 만들어진 object를 가리킬 수 있다
  • JS로 컴파일되면 ES5의 경우 function으로 변경된다

Constructor & Initialize

class Person {
	name: string = 'Jinju'; // 선언 시 동시에 값 할당
  	age!: number; // 클래스 내부에서 할당을 미루고 싶을 때는 !를 붙인다
	
	// 클래스에 생성자를 구현하지 않을 경우
	// > 보이지는 않지만 디폴트 생성자가 자동으로 생성

	// 하지만 개발자가 생성자를 따로 구현한다면
	// 디폴트 생성자는 사라진다

	// constructor(age: number) {
    //	this.age = age;  > 이런식으로 쓰면 age에 !는 빼도 무방
    // }
}

const p1 = new Person();
pl.age = 26; // undefined 뜨지 않으려면 클래스 외부에서 값 할당 해줘야 한다
console.log(p1.age);
const p1: Person = new Person(26);
const p2: Person = new Person();

p1, p2처럼 값을 넣어줄 수도 있고 안 넣어줄 수도 있게끔 한다면 오버로딩을 사용할 수 있겠으나 (참고로 JavaScript는 오버로딩 X)

constructor (age?: number) {
    if (age === undefined) { this.age = 20; }
	else { this.age = age; }
}

undefined 값이 number타입의 age에 할당되면 에러가 날 수 있으므로 조건문으로 한 번 걸러준다

⭐ 생성자에는 async를 붙일 수 없다 ⭐

  • 생성자 함수가 없으면, 디폴트 생성자가 불린다
  • 프로그래머가 만든 생성자가 하나라도 있다면, 디폴트 생성자는 사라진다
  • strict 모드에서는 프로퍼티를 선언하는 곳 또는 생성자에서 값을 할당해야 한다
  • 프로퍼티를 선언하는 곳 또는 생성자에서 값을 할당하지 않는 경우네느 !를 붙여서 위험을 표시한다
  • 클래스의 프로퍼티가 정의되어있지만, 값을 대입하지 않으면 undefined이다
  • 생성자에는 async를 설정할 수 없다

접근 제어자 (Access Modifiers)

class Person {
	public name: string = 'Jinju';
	private _age!: number;

	public async init() {}
}
  • 접근 제어자의 종류로는 public, private, protected가 있다
  • 설정하지 않으면 public이다
  • 클래스 내부의 모든 곳(생성자, 프로퍼티, 메서드)에 설정 가능
  • private으로 설정하면 클래스 외부에서 접근 불가
  • 자바스크립트에서는 private을 지원하지 않아 프로퍼티나 메서드 이름 앞에 '_'를 붙이는게 관습이었다

Initialization in Constructor Parameters

class Person {
	name: sring;
	age: number;

	public constructor(name: string, age: number) {
    	this.name = name;
      	this.age = age;
    }
}

// 위 아래의 코드는 같은 동작을 한다

class Person {
	public constructor(public name: string, public age: number) {}
}

Getters & Setters

class Person {
  
  	// _(언더바)를 붙여 getter와 setter 이름 중복 충돌을 피한다
	public constructor(public _name: string, private age: number) {}
  
  	get name() {
    	return this._name;
    }
  
  	set name(n: string) {
    	this._name = n;
    }
}

만약 setter를 지정하지 않는다면 > read only가 된다

Readonly Properties

class Person {
	public readonly name: string = 'Jinju';
	private readonly country: string;

	public constructor(private _name: string, private age: number) {
    	this.country = 'Korea'; // OK
    }

	hello() {
    	this.country = 'Canada'; // Error 
    }
}

readonly 키워드를 쓸 때는 public이든 private이든 초기화 되는 곳(생성자, 프로퍼티)에서만 값을 바꿀 수 있다.

언제 쓰는가?

👉 프로퍼티를 초기 값으로 고정하고 다른 값으로 변경하고싶지 않을 때

Index Signatures in Class

// class => Object
class Students {
	// [index: string]: string; 으로 해도 되지만 좀 더 정확하게는
  	[index: string]: 'male' | 'female';
}

const aClass = new Stduents();
aClass.john = 'male';
aClass.will = 'male';

console.log(aClass);
// {john: 'male', will: 'male'}

const bClass = new Students();
bClass.chloe = 'female';
bClass.alex = 'male';
bClass.anna = 'female';

console.log(bClass);
// {chloe: 'female', alex: 'male', anna: 'female'}

그래서 인덱스 시그니처란,

👉 프로퍼티가 고정된 형태가 아니라 동적으로 들어오는 경우에 고려해볼 수 있는 사항

Static Properties & Methods

class Person {
  	public static CITY = 'Incheon';
  
	public static hello() {
    	console.log('안녕');
    }

	public change() {
    	Person.CITY = 'Seoul';
    }
}

// 클래스에다가 대고 바로 메소드나 프로퍼티 호출 가능
Person.hello();
Person.CITY;

const p1 = new Person();
// p1.hello(); > ERROR

Singletons

어플리케이션 실행 중 클래스로부터 단 하나의 오브젝트만 생성해서 사용하는 패턴

class ClassName {
  	private static instance: ClassName | null = null;
  
  	// 매개체가 되어줄 getInstance 함수
  	public static getInstance(): ClassName {
      
    	// ClassName으로부터 만든 object가 있으면 그걸 리턴
      	// ClassName으로부터 만든 object가 없다면, 만들어서 리턴
      	if(ClassName.instance === null) {
          // class 내부이므로 private 생성자 호출 가능
        	ClassName.instance = new ClassName();
        }
      	return ClassName.instance;
    }
  
	private constructor() {}
}

// 생성자에 private 키워드가 붙었으므로 new 키워드 사용 불가
// const a = new ClassName();

// a = 최초로 만들어짐
const a = ClassName.getInstance();

// a로 인해 이미 생성되었으므로 그것을 리턴
// a와 b사이의 공유 오브젝트 > static 
const b = ClassName.getInstance();

console.log(a === b); // true
// a와 b 둘은 완벽하게 같은 오브젝트임이 증명됨

상속 (Inheritance)

class Parent {
	constructor(protected _name: string, private _age: number) {}
  
  	public print(): void {
    	console.log(`이름은 ${this._name}이고, 나이는 ${this._age} 입니다.`);
      
    protected printName(): void {
    	console.log(this._name);
    }
    }
}

const p = new Parent('Jinju', 26);
p.print(); // 이름은 Jinju이고, 나이는 26 입니다.
// 👉 생성자의 접근제어자가 protected, private 이어도 제대로 출력 됨 ✨

class Child extends Parent {
    // 오버라이드 시 부모클래스의 접근제어자를 바꿔도 무방
  	// public _name = 'example';

	public gender = 'male';
	
	// 생성자도 오버라이드 가능
	constructor(age: number) {
    	super('example', age); // < 부모의 생성자 호출
      	this.printName();
    }
}

Abstract Classes

abstract class AbstractPerson {
	protected _name: string = 'Jinju';
	
	// abstract 메서드는 구현하지 않는다
	// 미완성 메소드이므로 new 키워드로 생성 불가
	abstract setName(name: string): void;
}

// new AbstractPerson(); > 불가능
// 상속을 이용해 완전한 클래스로 만든 뒤 사용가능

class Person extends AbstractPerson {
	setName(name: string): void {
    	this._name = name;
    }
}

const p = new Person();
p.setName('Jinju');

0개의 댓글