class

class

class Player {
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string
    ) {}
}

const nico = new Player("Nico", "las", "니코");

nico.firstName; // private
//Property 'firstName' is private and only accessible within class 'Player'.

abstract class

abstract class 는 다른 class가 상속받을 수 있는 class이다.
그러나 abstract class는 거기에서 새로운 instance를 만들 수는 없다.

abstract class User { 
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string
    ) {}
}

//extend를 사용하여 상속받는다.
class Player extends User {
}

const nico_wrong = new User("Nico", "las", "니코");
//Cannot create an instance of an abstract class.
const nico = new Player("Nico", "las", "니코");

nico.nickname; // public

method

class 안에 존재하는 함수를 method라고 부른다.

abstract class User {
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string
    ) {}
    getFullName(): string {
        return `${this.firstName} ${this.lastName}`;
}

class Player extends User {
}

const nico = new Player("Nico", "las", "니코");

nico.getFullName(); // public

만약 private getFullName(): string 이라면 nico.getFullName();은 작동하지 않는다.

abstract method

메소드도 추상화시킬 수 있다. abstract class 안에서 method 를 완전히 구현하는 대신에 call signature만 적어주는 방법으로 가능하다.

abstract class를 상속받은 class에서 method를 구현해야 한다.

abstract class User {
    constructor(
        private firstName: string,
        private lastName: string,
        private nickname: string
    ) {}
    abstract getNickName(): void;

    getFullName(){
        return `${this.firstName} ${this.lastName}`;
}

class Player extends User {
    getNickName(): void {
        console.log(this.nickname)
        //Property 'nickname' is private and only accessible within class 'User'.
    }
}

property를 보호하는 방법

  • public: 인스턴스 밖에서 접근할 수 있다.
  • private: 인스턴스 밖에서 접근할 수 없고, 다른 자식 클래스에서도 접근할 수 없다.
    이를 선언한 abstract class에서만 접근 가능하다.
  • protected: 외부로부터는 보호되지만 다른 자식 클래스에서는 사용되길 원한다면 사용해야 할 방법
abstract class User {
    constructor(
        protected firstName: string,
        protected lastName: string,
        protected nickname: string
    ) {}
    abstract getNickName(): void;

    getFullName(){
        return `${this.firstName} ${this.lastName}`;
}

class Player extends User {
    getNickName(): void {
        console.log(this.nickname) //protected
				//abstract class를 상속받은 class 내부이기 때문에 접근할 수 있다.
    }
}

Dictionary 만들기

type Words = {
    [key: string]: string
}
//[key: string]
//제한된 양의 property 또는 key를 가지는 타입을 정의하는 방법
//property의 이름은 모르지만 type은 알고 있을 경우 사용

class Dict {
    private words: Words
    constructor() {
        this.words = {}
        //property가 constructor로부터 바로 초기화 되지 않는 경우
    }
    add (word: Word) {
        //class를 type처럼 사용할 수 있다.
        if (this.words[word.term] === undefined) {
            this.words[word.term] = word.def;
        }
    }
    def (term: string) {
        return this.words[term]
    }
}

class Word {
    constructor(
        public term: string,
        public def: string
    ) {}
}

const kimchi = new Word('kimchi', '한국의 음식')

const dict = new Dict()

dict.add(kimchi)
dict.def("kimchi")

readonly

class Word {
    constructor(
        public readonly term: string,
        public readonly def: string
    ) {}
}

const kimchi = new Word('kimchi', '한국의 음식')

kimchi.def = '맛있다.'
//cannot assign to 'def' because it is a read-only property

//public 이기 때문에 외부에서도 볼 수는 있지만 값을 바꿀수는 없게한다.

static method

class Dict {
    private words: Words
    constructor() {
        this.words = {}
        //property가 constructor로부터 바로 초기화 되지 않는 경우
    }
    add (word: Word) {
        //class를 type처럼 사용할 수 있다.
        if (this.words[word.term] === undefined) {
            this.words[word.term] = word.def;
        }
    }
    def (term: string) {
        return this.words[term]
    }
    static hello() {
        return "hello"
    }
}

...

Dict.hello()
//이런식으로 사용할 수 있다.

interface

TS에서 Type이 할 수 있는 일

//타입의 alias를 지정
type Nickname = string
type Health = number
type Friends = Array<string>

//타입이 특정 값만을 갖도록 지정할 수 있음
type Team = "red" | "blue" | "yellow"

//object의 형태와 타입 지정
type Player = {
    nickname: string,
    team: Team,
    healthBar: number,
}

//미리 지정해둔 형태의 타입을 사용
const nico: Player = {
    nickname: 'Nico',
    team: "red",
    healthBar: 10
}

object 의 형태를 지정하는 또 다른 방법, interface

interface Player {
    nickname: string,
    team: Team,
    healthBar: number,
}
//object의 형태를 지정하는 용도로만 사용되는 것이 interface

type과의 비교 - 상속

//interface
interface User {
    name: string;
}

interface Player extends User {
}

const nico : Player = {
    name: "Nico"
}
//type
type User = {
    name: string;
}

type Player = User & {
}

const nico : Player = {
    name: "Nico"
}

type 과의 비교 - 중첩

interface User {
    name: string;
}

interface User {
    lastName: string;
}

interface User {
    health : number;
}

const nico: User = {
    name: 'Nico',
    lastName: 'Gonzalez',
    health: 100
}
//이런식으로 같은 이름 User를 가진 interface를 여러번 만들어서
//이를 활용할 때 하나로 합칠 수 있다.

abstract class와의 비교 - JS compile

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()}`;
    }
}

이런식으로 코드를 작성했을 때, JS로 컴파일 된 코드에는
class User 가 추상화 되지 않고 사용되지도 않는 단순한 class로 남아있게 된다.

한편 interface는 JS로 컴파일 될 때 사라진다.
TypeScript에서만 존재하는 문법이기 때문이다. 그래서 더 가벼워진다. (파일 크기를 줄일 수 있다.)

interface User {
    firstName: string,
    lastName: string,
    sayHi(name: string): string,
    fullName(): string
}

interface Human {
    health: number
}

//extends 대신 implements
class Player implements User {
   constructor(
	//interface를 상속할 땐 property를 private, protect로 만들 수 없다.
	//public이 되어야 한다.
    public firstName: string,
    public lastName: string
		public health: number
   ) {}
   fullName(): string {
       return `${this.firstName} ${this.lastName}`;
   }
   sayHi(name: string): string {
         return `Hi ${name}, my name is ${this.fullName()}`;
   }
}

//인터페이스도 타입으로 지정할 수 있다.
function makeUser(user: User) {
    return user.sayHi('John');
}

정리

type과 interface를 활용하는 법

//type 으로 object 형상 정의 가능
type PlayerA = {
    name: string;
}

//type을 상속하여 형상 정의 가능
type PlayerAA = PlayerA & {
    lastName: string;
}

//type을 이용하여 object 생성
const player: PlayerAA = {
    name: 'John',
    lastName: 'Doe'
}

/////

//interface로 object 형상 정의 가능
interface PlayerB {
    name: string;
}
//interface를 상속하여 형상 정의 가능
interface PlayerBB extends PlayerB {
    lastName: string;
}

//interface를 한번 더 사용하여 property 추가 가능
//type으로는 불가능하다.
interface PlayerBB {
    health: number;
}

//interface를 이용하여 object 생성
const PlayerB: PlayerBB = {
    name: 'John',
    lastName: 'Doe',
    health: 100
}

abstract class를 대체하는 type과 interface

type PlayerA = {
    firstName: string;
}

interface PlayerB {
    firstName: string;
}

class UserA implements PlayerA {
    constructor(
        public firstName: string
    ) {}
}

class UserB implements PlayerB {
    constructor(
        public firstName: string
    ) {}
}

TS 커뮤니티에서는 class 나 object 의 모양을 정의해야 할 땐
interface를 사용하는 것을 권장한다.

polymorphism

제네릭을 사용하여 polymorphism 구현

//3. interface SStorage는 받은 제네릭 타입 <T>를 이용해 
//value의 타입을 제네릭으로 지정
interface SStorage<T> {
    [key: string]: T
}

//1. class LocalStorage를 초기화할 때 제네릭 타입 <T>를 받을 것이라고 선언
class LocalStorage<T> {
    //2. 앞서 선언한 제네릭 타입 <T>를 interface SStorage<T>에 상속 
    private storage: SStorage<T> ={}
    set(key: string, value: T) {
				if (this.storage[key] !== undefined) {
            throw new Error('already exist');
        }
        this.storage[key] = value;
    }
    remove(key: string) {
        delete this.storage[key]
    }
    get(key: string): T {
        return this.storage[key]
    }
    clear() {
        this.storage = {}
    }
}

const stringsStorage = new LocalStorage<string>();
stringsStorage.get('ket');
//call signature: (key: string): string
//call signature 선언은 제네릭이었지만 선언을 읽고 자동으로 string으로 바뀌었다.

stringsStorage.set('name', 'Lee');
//call signature: (key: string, value: string): void

const booleanStorage = new LocalStorage<boolean>();
booleanStorage.get('x');
//call signature: (key: string): boolean
//마찬가지로 제네릭 -> boolean으로 변경됨

booleanStorage.set('isPerson', true);
//call signature: (key: string, value: boolean): void
profile
Fear always springs from ignorance.

0개의 댓글

Powered by GraphCDN, the GraphQL CDN