제네릭
/**
* 제네릭
*/
// 제네릭 함수
function func<T>(value: T): T {
return value;
}
let num = func(2);
let bool = func(true);
let str = func('string');
let arr = func<[number, number, number]>([1, 2, 3]); // 튜플
위 함수에서 반환값의 타입은 함수 호출 시 결정된다.
튜플 타입을 넣어주고 싶은 경우, 타입 단언을 사용하는 것보다 위와 같이 나타내는 것이 더 좋다.
타입 변수 응용하기
/**
* 첫 번째 사례
*/
function swap<T, U>(a: T, b: U): [U, T] {
return [b, a];
}
const [a, b] = swap('1', 2);
/**
* 두 번째 사례
*/
function returnFirstValue1<T>(data: T[]): T {
return data[0];
}
let num1 = returnFirstValue1([1, 'hello', 'hi']); // string | number 타입
function returnFirstValue2<T>(data: [T, ...unknown[]]): T {
return data[0];
}
let num2 = returnFirstValue2([1, 'hello', 'hi']); // number 타입
/**
* 세 번째 사례
*/
function getLength<T extends { length: number }>(data: T) {
return data.length;
}
let var1 = getLength([1, 2, 3]);
let var2 = getLength({ length: 10 });
let var3 = getLength('1234');
세 번째 사례의 경우, extends 키워드를 사용해서 T의 범위를 제한하는 것이다.
map, forEach 메서드 타입 정의하기
/**
* map 메서드
*/
const arr = [1, 2, 3];
const newArr = arr.map((it) => it * 2);
function map<T, U>(arr: T[], callback: (item: T) => U) {
let result = [];
for (let i = 0; i < arr.length; i++) {
result.push(callback(arr[i]));
}
return result;
}
map(arr, (it) => it * 2);
map(['hi', 'hello'], (it) => parseInt(it));
/**
* forEach 메서드
*/
const arr2 = [1, 2, 3];
arr2.forEach((it) => console.log(it));
function forEach<T>(arr: T[], callback: (item: T) => void) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i]);
}
}
forEach(arr2, (it) => console.log(it.toFixed()));
제네릭 인터페이스, 제네릭 타입 별칭
/**
* 제네릭 인터페이스
*/
interface KeyPair<K, V> {
key: K,
value: V
}
let keyPair1: KeyPair<string, number> = {
key: 'key',
value: 0
}
let keyPair2: KeyPair<boolean, string[]> = {
key: true,
value: ['1']
}
/**
* 인덱스 시그니처
*/
interface NumberMap {
[key: string]: number;
}
let numberMap1: NumberMap = {
key1: -123,
key2: 23,
}
interface Map1<V> {
[key: string]: V
}
let stringMap: Map1<number> = {
key1: 123,
key2: 345
}
/**
* 제네릭 타입 별칭
*/
type Map2<V> = {
[key: string]: V
}
let stringMap2: Map2<string> = {
key1: 'hello',
key2: 'hi'
}
/**
* 제네릭 인터페이스의 활용 예시
*/
interface Student {
type: 'student',
school: string;
}
interface Developer {
type: 'developer',
skill: string;
}
interface User<T> {
name: string;
profile: T;
}
function goToSchool(user: User<Student>) {
const school = user.profile.school;
console.log(`${school}로 등교 완료`);
}
const developerUser: User<Developer> = {
name: '이정환',
profile: {
type: 'developer',
skill: 'typescript'
}
}
const studentUser: User<Student> = {
name: '홍서현',
profile: {
type: 'student',
school: '홍익대학교'
}
}
제네릭 인터페이스/제네릭 타입 별칭을 사용하는 경우에는 무조건 <> 안에 타입을 지정해서 사용해야 한다.
제네릭 인터페이스를 사용하면, 보다 간단하게 코드를 작성할 수 있다.
goToSchool 같은 함수에서 제네릭 인터페이스를 사용하지 않고 인터페이스의 유니온을 사용해서 매개변수로 User를 받는 경우에는 타입 가드를 해주어야 한다.
하지만, 제네릭 인터페이스를 사용해서 매개변수로 User<Student>로 받는 경우에는 User<Developer>의 경우를 고려해줄 필요가 없게 된다.
제네릭 클래스
/**
* 제네릭 클래스
*/
class List<T> {
constructor(private list: T[]) { }
push(data: T) {
this.list.push(data);
}
pop() {
return this.list.pop();
}
print() {
console.log(this.list);
}
}
const numberList = new List<number>([1, 2, 3, 4]);
numberList.pop();
numberList.push(5);
numberList.print();
제네릭 클래스의 경우, 클래스의 생성자 호출 시 인수로 전달하는 값으로 타입을 추론하게 된다.
따라서, 제네릭 타입 별칭과 제네릭 인터페이스와는 다르게 타입을 별도로 명시해주지 않아도 된다.
프로미스와 제네릭
/**
* 프로미스
*/
const promise = new Promise<number>((resolve, reject) => {
setTimeout(() => {
//resolve(20);
reject('실패 원인 ...');
}, 3000);
});
promise.then((response) => {
console.log(response * 10);
})
promise.catch((err) => {
if (typeof err === 'string') {
console.log(err);
}
})
/**
* 프로미스를 반환하는 함수의 타입을 정의
*/
interface Post {
id: number;
title: string;
content: string;
}
function fetchPost(): Promise<Post> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
title: '1',
content: '1'
})
}, 3000);
})
}
const postRequest = fetchPost();
postRequest.then((post) => {
post.id
});
resolve의 경우, 타입을 기본적으로는 unknown 타입으로 추론한다.
이때 제네릭을 사용하면 문제를 해결할 수 있다.
위 코드와 같이 프로미스 결과값의 타입을 지정해주면 된다.
'Frontend > Typescript' 카테고리의 다른 글
조건부 타입과 유틸리티 타입 (0) | 2023.06.23 |
---|---|
타입 조작하기 (0) | 2023.06.23 |
인터페이스와 클래스 (0) | 2023.06.20 |
함수와 타입 (0) | 2023.06.20 |
타입스크립트 이해하기 (0) | 2023.06.20 |