8/ Generic

8/ Generic

목차

타입스크립트 정리 글은 이웅재님의 강의강의록을 참고하여 작성하였습니다. (짱짱)
오류가 있다면 언제든지 댓글 부탁드립니다.

요약
제네릭은 어떠한 클래스 혹은 함수에서 사용할 타입을 그 함수나 클래스를 사용할 때 결정하는 프로그래밍 기법을 말한다. 정적 타입 언어에서도 이렇게 특정 타입을 위해 만들어진 함수 혹은 클래스를 보다 범용적으로 재사용하기 위한 요구가 있기 때문에 제네릭이라는 프로그래밍 기법이 생긴 게 아닐까한다.


1. any => generic

any의 사용을 지양하고자 타입을 인자로 넘긴다.

  • 탬플릿을 만드는 개념. 인자값과 출력값의 타입을 같게 탬플릿을 만들어준다.
  • 제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다. 한번의 선언으로 다양한 타입에 재사용이 가능하다는 장점이 있다.
  • T는 제네릭을 선언할 때 관용적으로 사용되는 식별자로 타입 파라미터(Type parameter)라 한다. T는 Type의 약자로 반드시 T를 사용하여야 하는 것은 아니다.
  • 함수에도 제네릭을 사용할 수 있다. 제네릭을 사용하면 하나의 타입만이 아닌 다양한 타입의 매개변수와 리턴값을 사용할 수 있다.
1
2
3
function helloGeneric<T>(message: T): T{
return message;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function helloString(message: string): string {
return message;
}

function helloNumber(message: number): number {
return message;
}

// 더 많은 반복된 함수들 ...

function hello(message: any): any {
return message;
}

function helloGeneric<T>(message: T): T {
return message;
}

console.log(hello('Mark').length);
console.log(hello(35).length); // hello 의 리턴이 any 이기 때문에 타입 헬퍼가 제대로 되지 않음

console.log(helloGeneric(35).toString()); // console.log(helloGeneric<number>(35).toString());
// helloGeneric 을 사용하면 정상적으로 사용가능

2. basic generic

  1. Generic 타입을 쓰지 않으면, T 로 추론
  2. Generic 타입을 쓰면, T 를 확인
1
2
3
4
5
6
7
8
9
10
11
function helloGeneric<T>(message: T): T {
return message;
}

function hello<T>(message: T): T {
return message;
}

console.log(hello<string>('Hello'));
let age = hello(35);
hello<number>('35');

3. Generic Array

hello 함수의 제네릭 타입을 [] 를 이용하여 배열로 사용할 수 있음

1
2
3
4
5
function hello<T>(messages: T[]): T {
return messages[0];
}

console.log(hello<string>(['Hello', 'World']));

4. Generic Type

구현체에 return T 를 설정하지 않아도, return false 시 오류가 나지 않는다?

  • 때에 따라서 적합하지 않을 수 있다.
1
2
3
4
5
6
7
type HelloGeneric = <T>(message: T) => T;

const hello: HelloGeneric = <T>(message: T): T => {
return message;
}

console.log(hello<string>('Hello').length);

5. Generic Class

명시적으로 제네릭 타입을 설정하면 오류

  • function에서 generic을 사용할 때의 오류와 같다.
1
2
3
4
5
6
7
8
9
10
11
class Person<T> {
private _name: T;
private _age: number;

constructor(name: T) {
this._name = name;
}
}

new Person('Mark');
// new Person<string>(35);

6. Generic with extends

T 가 string 또는 number 를 상속받기 때문에 boolean 은 안된다.

1
2
3
4
5
6
7
8
9
10
11
12
class Person<T extends string | number> { // union type
private _name: T;
private _age: T;

constructor(name: T) {
this._name = name;
}
}

new Person('Mark');
new Person(35);
// new Person(true);

7. Generic with multiple types

1
2
3
4
5
6
7
8
9
10
11
class Person<T, K> {
private _name: T;
private _age: K;

constructor(name: T, age: K) {
this._name = name;
this._age = age;
}
}

new Person('Mark', 35);

8. type lookup system

keyof 키워드를 알아야한다.

1
2
type Test = keyof Person;
// 리터럴 타입의 유니온 타입이 나온다. "name" | "age"

객체와 key값을 인자로 받아서 perperty의 타입값을 알아내는 함수를 만들었다고 치자.
함수에서 컴파일 타입을 검증할 수 있는 시스템이 필요하다. => type lookup system

  • getProperty: Generic과 type alias를 결합하여 사용하여 type을 찾아낼 수 있는 시스템을 만든다.
  • setProperty: Generic과 type alias를 결합하여 사용하여 type을 찾아내고, 타입을 다시 재정의 하는 함수
1
2
3
4

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Person {
name: string;
age: number;
}

const person: Person = {
name: 'Mark',
age: 35
};

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}

function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
obj[key] = value;
}

console.log(getProperty(person, 'name'));
// console.log(getProperty(person, fullname));
setProperty(person, 'name', 'Anna');
console.log(getProperty(person, 'name'));
// setProperty(person, 'name', 24);

참고링크

  1. http://poiemaweb.com/typeScript-generic
  2. https://www.youtube.com/watch?v=3-nJyzJATq8
  3. https://hyunseob.github.io/2017/01/14/typeScript-generic/
📚