본문 바로가기
공부/JS

Typescript 정리 - 2

by Piva 2023. 1. 16.

Call Signatures (or Function Signatures)

: 함수의 패러미터 타입 & return 타입을 type으로 선언하는 것.

type Foo = (a: number, b: number) => number;

const foo:Foo = (a, b) => {
	// return number...
};

→ foo 함수의 패러미터의 타입을 지정해주지 않아도 Foo 타입을 통해 TS가 패러미터의 타입을 알 수 있음.

→ 함수 타입 선언과 구현을 분리할 수 있음.

 

Overloading

  • 함수가 여러 개의 서로 다른 Call Signature를 가질 때 발생.
  • 패러미터가 각각 다른 같은 이름의 함수를 오버로딩 함수라 부름.
  • 패키지나 라이브러리 디자인에 많이 사용됨.
type Foo = {
	(a: number, b: number) : number
	(a: number, b: string) : number
}

→ Call Signature를 정의할 때는 위의 형식으로 여러 개의 signature를 정의할 수 있음.

→ Signature 끼리 다른 타입의 패러미터를 가질 경우, 함수 구현 시 타입을 체크하는 과정 필요.

 

type Foo = {
	(a: number, b: number) : number
	(a: number, b: number, c: number) : number
}

// a, b는 모든 signature에 포함되어 있으므로 c는 선택 패러미터
const foo: Foo = (a, b, c?: number) => {
	if (c) {
		return a + b + c;
	}

	return a + b;
}

→ Signature끼리 서로 다른 개수의 패러미터를 가질 수도 있음.

→ 함수 구현 시, 선택적인 패러미터의 예상 타입을 지정.

 

Polymorphism (다형성)

: “다른 모양의 함수를 가질 수 있도록 하는 것”.

→ 함수가 다양한 패러미터를 가질 수 있는 것 또한 다형성.

 

다형성의 구현

  • generic을 이용해서 다형성을 구현하는 것이 가능.
    → generic : 타입의 placeholder 같은 것.
    ⇒ 다양한 타입을 수용하는 컴포넌트의 구현이 가능.
    ⇒ Call Signature의 타입을 미리 알 수 없는 경우에 사용할 수 있음.
    ⇒ TS가 입력된 타입을 추론함.
type Foo = {
	<T> (arr: T[]): void
}

→ generic을 사용할 때는 <(Generic의 이름)>

→ <> 안에는 어떤 것이 들어가도 상관 없으나 보통 T, V를 많이 씀.

→ 타입 선언에 들어갈 특정 타입 대신 generic type을 넣음(타입을 치환한다는 느낌).

→ generic에 들어온 타입을 TS가 추론한 타입으로 바꾸어서 사용.

⇒ generic을 사용함으로써 함수가 다양한 call signature를 갖게 됨 == 다형성의 구현

 

※ 타입에 상관없다는 점에서 any와의 차이가 없어 보이지만, any를 사용할 경우
- 타입 추론이 일어나지 않음.
- TS의 보호를 받을 수 없음(에러 발생할 수 있음).
  • TS는 generic의 순서와 generic을 처음 인식했을 때를 기반으로 타입을 추론함.

 

Class와 Interface

Class

// TypeScript
class Person {
    constructor (
        private name: string,
        private age: number,
    ) {}
}

// 위의 코드가 JS로 변환될 경우
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

→ 기존의 JS에서 직접 작성해주어야 했던 constructor를 알아서 작성.

 

Abstract class

: 추상 클래스.

  • 다른 곳에서 상속 받을 것만 가능한 클래스.
  • 인스턴스를 만들 수 없음.
  • 추상 클래스 내에 구현된 메소드는 이를 상속받은 자식 클래스에서 그냥 사용할 수 있음.
  • class 앞에 abstract를 붙임으로써 구현 가능.
abstract class Person {
    constructor (
        private name: string,
        private age: number,
    ) {}
}

 

Abstract method

: 구현이 되어 있지 않은 메소드.

  • 구현이 되어 있지 않기 때문에 call signature만 가지고 있음.
  • 상속받은 자식이 이를 구현할 필요가 있음.
    → 구현하지 않으면 에러 발생.
abstract class Person {
    constructor (
        private name: string,
        private age: number,
    ) {}
	abstract eat(): void;
}

 

접근 제어자

  • TS는 private, public, protected 의 접근 제어자를 제공함.
    • 위의 코드에서, JS는 접근 제어자를 제공하지 않기 때문에 private이 일괄적으로 모두 사라진 것을 확인 가능.

 

public

: 모든 범위에서 멤버에 접근하는 것이 가능.

 

private

: 해당 클래스 내에서만 접근 가능.

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

const man = new Person("John", 30);
console.log(man.name); // 클래스 바깥에서 name에 접근할 수 없기에 에러 발생.

 

protected

: 해당 클래스 내 혹은 자식 클래스(해당 클래스를 상속받은 클래스)에서의 접근이 가능.

class Person {
    constructor (
        protected name: string,
    ) {}
}

// Person 클래스를 상속받은 Student 클래스에서 protected 인 name에 접근하는 것이 가능.
class Student extends Person {
    introduce() {
        console.log(`My name is ${this.name}`);
    }
}

 

※ 제한된 양의 property / key를 가지는 타입을 정의하는 방법

// string 타입만을 key와 value로 갖는 오브젝트 타입 정의
type Foo = {
	[key: string]: string
}

/*
const foo: Foo = {
	"apple": "delicious"
};
*/

 

Interface

  • 오브젝트 혹은 클래스의 형태를 정의할 때 사용.
  • JS로 변환할 시 사라짐(TS에서만 존재).
  • 타입으로 활용하는 것이 가능.
  • 클래스와 비슷하며, 상속도 가능.
interface Person {
    name: string,
    age: number,
}

class Student extends Person {
	/* ... */
}

 

type과 interface의 차이점

  • interface는 오직 오브젝트의 형태 정의에만 사용됨.
  • 반면 type은 interface보다 유연하며, 다양한 용도로 사용될 수 있음.
// type 활용 예시
type Age = number;

class Person {
    constructor (
        private name: string,
        private age: Age,
    ) {}
}

type RgbColor = "red" | "green" | "blue"; // type에 concrete값을 설정하는 것도 가능.

 

  • type을 이용해 interface 상속을 구현하는 것도 가능.
type Person = {
    name: string,
    age: number,
}

type Student = Person & {
	/* ... */
}

 

  • interface는 property를 축적시키는 것이 가능(type은 불가).
interface Person {
	name: string
}

interface Person {
	age: number
}

interface Person {
	job: string
}

// 위에서 만든 3개의 property를 모두 사용 가능
const foo: Person = {
	name: "John",
	age: 20,
	job: "Student",
};

 

interface를 활용한 abstract class 구현

  • abstract class는 JS로 변환되면 일반 class로 변환됨.
  • interface는 JS로 변환되면 그냥 사라지기 때문에, abstract 클래스를 비슷하게 구현하면서도 JS로 변환된 코드는 더욱 작게 유지할 수 있음.
// abstract class
abstract class Person {
    constructor (
        protected name: string,
        private age: number,
    ) {}
	abstract introduce(): string;
}

class Student extends Person {
		introduce() {
				return `Hi, my name is ${this.name}.`;
		}
}
// using interface
interface Person {
    name: string,
    age: number,
		introduce(): string;
}

// interface에서 정의한 name, age 등의 프로퍼티와 메소드를 클래스에서 가지도록 강제함.
class Student implements Person {
    constructor (
        public name: string,
        public age: number,
    ) {}
		introduce() {
			return `Hi, my name is ${this.name}.`;
		}
}

⇒ interface를 사용할 경우 private property를 사용할 수 없으며, constructor가 존재하지 않기 때문에 interface를 구현하는 클래스에서 constructor를 작성해야 함.

⇒ interface는 여러 개를 상속하는 것이 가능.

 


  • 휘릭휘릭 정리한 게 많아서 헷갈리는 것도 좀 있고 나중에 다시 정독 한 번 해봐야 할듯.
  • 생각보다 예전 JAVA에서 배웠던 내용이 많이 나오고, 객체지향적 성격을 띄는 부분이 많았던 것 같다.

'공부 > JS' 카테고리의 다른 글

[Javascript] 클로저 알아보기 - 1  (1) 2024.01.05
[JavaScript] 실행 컨텍스트에 대하여  (2) 2023.12.23
Typescript 정리 - 1  (1) 2023.01.10
Clean Code Study - 2  (0) 2023.01.03
Clean Code Study - 1  (1) 2022.10.04