Classes
TypeScript는 JavaScript 객체 지향 프로그래밍(OOP)을 위한 다양한 기능을 지원한다
class Player {
constructor(
private firstName: string,
private lastName: string,
public nickName: string
) {}
}
이렇게 TS에서 클래스의 private/public 속성(property), 타입을 정의하는 것 만으로..
class Player {
constructor(firstName, lastName, nickName) {
this.firstName = firstName;
this.lastName = lastName;
this.nickName = nickName;
}
}
JS 상에서 클래스의 property가 정의되고 동작한다!
const me = new Player("sungjoong", "kim", "joseph")
//error
me.firstName;
//pass
me.nickName;
인스턴스를 생성, public/private 여부에 따라 클래스 외부에서의 property 접근이 제한된다
abstract class User {
constructor(
private firstName: string,
private lastName: string,
public nickName: string
) {}
private getFullName(){
return `${this.firstName} ${this.lastName}`
}
}
class Player extends User {
}
// error
const me = new User("sungjoong", "kim", "joseph");
// error
me.getFullName();
인스턴스를 직접 생성할 수 없고, 다른 클래스가 상속만 받을 수 있는 추상 클래스도 정의할 수 있다
* 메서드(method)에도 private/public 지정이 가능
abstract class User {
constructor(
private firstName: string,
private lastName: string,
protected nickName: string
) {}
abstract getNickName(): string
getFullName(){
return `${this.firstName} ${this.lastName}`
}
}
class Player extends User {
getNickName() {
return this.nickName;
}
}
const me = new Player("sungjoong", "kim", "joseph");
me.getNickName;
추상 클래스에 메서드의 Call Signature를 넣는 방식으로 추상 메서드를 정의할 수 있다
public/private/protected의 property/method 접근 차이
- public: 정의된 클래스 외부에서 접근할 수 있다 (기본값)
- private: 정의된 클래스 내부에서만 접근할 수 있다
- protected: 정의된 클래스 내부 + 추상 클래스를 상속받은 클래스 내부에서 접근할 수 있다
type Words = {
[key: string]: string
}
class Dict {
private words: Words;
constructor() {
this.words = {};
}
add(word: Word) {
if(this.words[word.term] === undefined){
this.words[word.term] = word.def;
}
}
def(term: string) {
return this.words[term];
}
static hello() {
return "hello";
}
}
class Word {
constructor(
public readonly term: string,
public readonly def: string
) {}
}
const kimchi = new Word("김치", "한국의 음식");
const dict = new Dict();
dict.add(kimchi);
dict.def("김치");
Dict.hello();
- 객체의 전체 타입 지정 (index signature) 타입 선언
- constructor에 포함되지 않은 형태로 property를 정의하고, 이후 수동으로 초기화
- 클래스를 타입처럼 사용 (클래스의 인스턴스가 매개변수인 경우)
- 클래스의 static 메서드/변수는 인스턴스와 관계없이 호출 가능
- 변경을 원하지 않는다면 property에서도 readonly 속성을 활용
Interfaces
type Team = "red" | "blue" | "yellow"
type Health = 1 | 5 | 10
type Player = {
nickname: string,
team: Team,
health: Health
}
const me: Player = {
nickname: "joseph",
team: "blue",
health: 5
}
concrete type이 아닌, 특정 값들 중에서만 선택할 수 있도록 타입을 지정할 수도 있다
interface Player {
nickname: string,
team: Team,
health: Health
}
오로지 객체의 모양을 설명하기 위한 목적으로 type을 interface로 대체할 수 있다
interface User {
name: string
}
interface Player extends User {
}
type User2 = {
name: string
}
type Player2 = User2 & {
}
type과 달리 클래스처럼 상속이 가능하다
interface User {
name: string
}
interface User {
age: number
}
const me: User = {
name: "joseph",
age: 23
}
type과 달리 중복 선언이 가능하며, 자동으로 합쳐진다 (= property가 쌓인다)
추상 클래스는 JS로 컴파일시 일반 클래스로 남는 반면, interface는 컴파일시 사라진다
즉, 추상 클래스를 interface로 대체할 수 있다면 JS 코드가 더 가벼워진다!
abstract class User {
constructor(
protected firstName: string,
protected lastName: string
) {}
abstract sayHi(name: string): string
abstract fullName(): string
}
class Player extends User {
fullName() {
return `${this.firstName} ${this.lastName}`;
}
sayHi(name: string) {
return `Hello ${name}. My name is ${this.fullName()}`;
}
}
위와 같은 추상 클래스를 interface로 대체해보자
interface User {
firstName: string,
lastName: string,
sayHi(name: string): string,
fullName(): string
}
class Player implements User {
constructor(
public firstName: string,
public lastName: string,
) {}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
sayHi(name: string) {
return `Hello ${name}. My name is ${this.fullName()}`;
}
}
이처럼 클래스가 interface를 implements로 상속받고, property를 constructor에 public으로 정의해주면 된다 (type도 동일하게 가능)
interface User {
firstName: string,
lastName: string,
sayHi(name: string): string,
fullName(): string
}
interface Human {
health: number
}
class Player implements User, Human {
constructor(
public firstName: string,
public lastName: string,
public health: number
) {}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
sayHi(name: string) {
return `Hello ${name}. My name is ${this.fullName()}`;
}
}
function makeUser (user: User){
console.log(user);
}
makeUser({
firstName: "kim",
lastName: "sungjoong",
fullName: () => "full name",
sayHi: (name) => `hi ${name}`
});
클래스는 여러 개의 interface들을 동시에 상속받을 수 있다!
* interface 역시 매개변수의 타입처럼 사용이 가능하며, 클래스처럼 매개변수에 인스턴스를 생성해 넣어줄 필요 없이 interface의 형태만 맞추면 동작
🔽공식문서 Type Aliases vs Interfaces 비교
interface는 type과 거의 유사하고 대체될 수 있지만 클래스나 객체의 형태를 정의하는 경우, 재선언이 가능하고 확장이 직관적인 interface 사용을 권장한다
Local Storage API 클래스를 가정해 타입을 만들어보며 다형성(Polymorphism)을 구현해보자
interface IStorage<T> {
[key: string]: T
}
class LocalStorage<T> {
private storage: IStorage<T> = {}
set(key: string, value: T) {
this.storage[key] = value;
}
remove(key: string) {
delete this.storage[key];
}
get(key: string): T {
return this.storage[key];
}
clear() {
this.storage = {};
}
}
const stringStorage = new LocalStorage<string>();
stringStorage.set("hello", "hi");
stringStorage.get("hello");
const booleanStorage = new LocalStorage<boolean>();
booleanStorage.set("hello", true);
booleanStorage.get("hello");
클래스에 할당한 Generic을 클래스에 속한 interface가 받아서 공유할 수 있다!
본 내용은 노마드코더의 'Typescript로 블록체인 만들기'를 바탕으로 작성되었습니다.
'Languages > TypeScript' 카테고리의 다른 글
[wanted] #1 Document for TypeScript (0) | 2022.09.05 |
---|---|
[노마드코더] #5 Typescript Blockchain (0) | 2022.05.27 |
[노마드코더] #3 Functions (0) | 2022.05.15 |
[노마드코더] #2 Overview of TypeScript (0) | 2022.05.14 |
[노마드코더] #1 Introduction (0) | 2022.05.03 |