- 노마드 코더의 TypeScript로 블록체인 만들기 강의에 나오는 TS 강의를 듣고 정리한 내용 백업.
- 범위는 FUNCTIONS ~ CLASSES AND INTERFACES.
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 |