Skip to content

Commit

Permalink
Merge pull request #50 from Orchemi/master
Browse files Browse the repository at this point in the history
#47 Post. Advanced TypeScript
  • Loading branch information
JaeKP authored Aug 4, 2022
2 parents 3d3de09 + 03bec18 commit 60041a2
Showing 1 changed file with 347 additions and 0 deletions.
347 changes: 347 additions & 0 deletions 박승훈/220729.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
# TypeScript 고급

<!-- 발표 영상: https://www.youtube.com/watch?v=wmTkIlY9lXU -->

## 제네릭(Generics)

### 제네릭이란?

> 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징
**한 가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트 생성에 사용**

<br>

```js
// JavaScript
function logText(text) {
console.log(text);
return text;
}

logText('Hello');
logText(10);
logText(true);
```

- JS 문법의 경우 위와 같은 함수에서는 어떤 것이든 인자로 사용 가능

<br>

```ts
// TypeScript
function logText<T>(text: T): T {
console.log(text);
return text;
}

logText<string>('Hello');
```

- TS의 generic을 활용하면 **함수를 호출할 때 type을 정의하겠다**는 약속
- 함수를 추상화해서 더 다양한 type에서 사용 가능

<br>

![image](https://user-images.githubusercontent.com/86189596/173964717-a4c8de60-89e0-4b87-86ec-4c0109d70768.png)

- 함수 호출에서 type을 정의하면 해당하는 type의 전달과 출력이 type으로 지정

<br>

### 기존 TS 함수의 중복 선언

```js
function logText(text: string) {
console.log(text);
return text;
}

function logNumber(num: number) {
console.log(num);
return num;
}

logText('a')
logNumber(10)
```

- 기존 TS 문법은 type에 따라 함수를 중복 정의 해야한다.
- 유지보수 차원에서 좋지 않음

<br>

### 유니온 타입을 이용한 선언 방식 문제점

```ts
function logText(text: string | number) {
console.log(text);
return text;
}
```

> 문제점1. 지정된 여러 type이 공통으로 가지는 method에서만 preview 지원
![image](https://user-images.githubusercontent.com/86189596/173974315-b5d3e702-f8a2-4973-9cb6-a3deecd59a2f.png)

<br>

> 문제점2. 한 가지 타입에서만 존재하는 메서드 사용 불가
![image](https://user-images.githubusercontent.com/86189596/173974478-986beeef-1030-488e-9c90-277e99a356d1.png)

- 해당 함수의 결과값은 union type이므로 한 가지 type에서만 지원하는 메서드 사용 불가

<br>

### 제네릭의 장점과 타입 추론에서의 이점

```ts
function logText<T>(text: T): T {
console.log(text);
return text;
}

const str = logText<string>('a')
str.split('')

const num = logText<number>(10)
num.toLocaleString()
```

- generic을 통한 type 정의를 통해 return된 값을 변수로 지정
- 이 변수는 각 type에서 지원하는 method 사용 가능

![image](https://user-images.githubusercontent.com/86189596/174127271-80278ded-b588-43da-9279-1f610984da11.png)

<br>

### 인터페이스에 제네릭 선언

```ts
interface Dropdown<T> {
value: T;
selected: boolean;
}

const obj1: Dropdown<string> = { value: 'abc', selected: false };
const obj2: Dropdown<number> = { value: 10, selected: false };
```

<br>

### 제네릭의 타입 제한

```ts
function logTextLength<T>(text: T): T {
console.log(text.length);
return text;
}

logTextLength('hi');
```

- 함수 내부에서 인자의 메서드 사용은 인자의 type마다 다르다.
- generic에서 type은 호출 때 정의되므로, 함수 작성 시에는 오류 발생

![image](https://user-images.githubusercontent.com/86189596/174138959-ca4af74a-ed1e-423a-a748-374fb2e61ac4.png)

<br>

> 힌트를 주자!
- text는 length를 셀 수 있는 애라는 것을 알린다!
- text는 배열이라고 가정

```ts
function logTextLength<T>(text: T[]): T[] {
console.log(text.length);
text.forEach(function (text) {
console.log(text);
})
return text;
}

logTextLength<string>(['hi', 'abc']);
```

- 실제 인자도 Array 형태로 전달해야 한다.

<br>

### 제네릭의 타입 제한2 : 정의된 타입 이용

```ts
interface LengthType {
length: number;
}

function logTextLength<T extends LengthType>(text: T): T {
text.length;
return text;
}

logTextLength('abcdef');
logTextLength(['a', 'b', 'c', 'd'])
logTextLength({ length: 10 })
logTextLength(10);
```

- 정의될 Type은 length 속성/메서드를 가진 LengthType의 상속을 받는 type이 된다.
- T type은 LengthType에 추가로 정의

![image](https://user-images.githubusercontent.com/86189596/174140574-08be9372-f30c-4fd0-9803-624eca70aeb1.png)

<br>

### 제네릭의 타입 제한3 : keyof 이용

```ts
interface ShoppingItem {
name: string;
price: number;
stock: number;
}

function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption: T): T {
return itemOption;
}
getShoppingItemOption('name');
```

interface의 key들 중 하나를 인자로 사용

<br>

## Promise를 이용한 API 함수 타입 정의

```ts
function fetchItems(): Promise<string[]> {
let items = ['a', 'b', 'c'];
return new Promise(function (resolve) {
resolve(items);
})
}
fetchItems();
```

<br>

## enum을 이용한 타입 정의

```ts
findContactByPhone(phoneNumber: number, phoneType: string): Contact[] {
return this.contacts.filter(
contact => contact.phones[phoneType].num === phoneNumber
);
}
```

<br>


이런 함수의 경우 인자로 phoneType을 받는다.

```ts
findContactByPhone('Homee');
```

그런데 이런 경우 오타를 방지할 수 없기 때문에 아래와 같이 enum을 사용

<br>

```ts
enum PhoneType {
Home = 'home',
Office = 'office',
Studio = 'studio',
}
```

```ts
findContactByPhone(PhoneType.Home);
```

오타에 대한 에러를 바로 확인 가능

<br>

## 타입 추론(Type Inference)

### 타입 추론이란?

```ts
// parameter의 type으로 return 값의 type이 결정
function getB(b) {
return b;
}
```

```ts
// parameter의 type으로 return 값의 type이 결정
function getB(b) {
const c = 'hi'
return b + c;
}
```

- number + string = string으로 type 추론
- return 값이 내부적으로 string으로 추론

<br>

### 인터페이스와 제네릭에서의 타입 추론

> 인터페이스와 제네릭
```ts
interface Dropdown<T> {
value: T;
title: string;
}

const shoppingItem: Dropdown<string> = {
value: 'abc',
title: 'hello',
}
```

<br>

> 인터페이스의 상속
```ts
interface Dropdown<T> {
value: T;
title: string;
}

// 관행적으로 T를 쓰지만 구분을 위해 K 사용
interface DetailedDropdown<K> extends Dropdown<K> {
description: string
tag: K;
}

const detailedItem: DetailedDropdown<string> = {
value: 'abc',
title: 'hello',
description: '안녕하세요~~',
tag: 'a'
}
```

인터페이스 A를 상속받은 인터페이스 B는 generic의 Type도 상속 가능

<br>

### Best Common Type

> 배열 내에 type이 여러 종류가 있는 경우
```ts
const arr: (number | string | boolean)[] = [1, 2, 3, 'a', 'b', 'c', true];
```

유니온 타입을 이용해 배열에 들어간 개체들의 타입을 추론해 union type으로 정의

![image](https://user-images.githubusercontent.com/86189596/174268126-33d74c33-0a86-45fc-919e-3d08201a55fc.png)

0 comments on commit 60041a2

Please sign in to comment.