TypeScript 개념 정리 - 제네릭

dobby·2025년 6월 23일
0

타입스크립트

목록 보기
6/11
post-thumbnail

제네릭

제네릭 함수: 모든 타입에 두루두루 사용할 수 있는 범용 함수

제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.

함수를 호출할 때 매개변수에 따라 타입이 달라진다.

function func<T>(value: T): T {
 return value; 
}

let num = func(10); // number type
let bool = func(true); // boolean type
let str = func('str'); // string type

타입 변수 응용

<T>: 타입 변수

function swap<T, U>(a: T, b: U) {
 return [b, a]; 
}

const [a, b] = swap('1', 2);
function returnFirstValue<T>(data: [T, ...unknown[]]) {
 return data[0]; 
}

let num = returnFirstValue([0, 1, 2]);
let str = returnFirstValue([1, 'hello', 'mynameis']);
function getLength<T extends {length: number}>(data: T) {
 return data.length; 
}

let var1 = getLength([1,2,3]);
let var2 = getLength('12345');
let var4 = getLength({length: 10});

map, forEach 메서드 타입 정의하기

// map
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
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());
});

forEach(['123', '456'], (it) => {
 it; 
})

제네릭 인터페이스 & 제네릭 타입 별칭

제네릭 인터페이스

interface KeyPair<K, V> {
 key: K;
 value: V;
}

let keyPair: KeyPair<string, number> = {
 key: 'key',
 value: 0,
}

let keyPair2: KeyPair<boolean, string[]> = {
 key: true,
 value: ['1'],
}

인덱스 시그니처

interface NumberMap {
 [key: string]: number;
}

let numberMap1: NumberMap = {
 key: -1234,
 key2: 123123,
}

interface Map<V> {
 [key: string]: V; 
}

let stringMap: Map<string> = {
 key: 'string', 
}

let booleanMap: Map<boolean> = {
 key: true, 
}

제네릭 타입 별칭

type Map2<V> = {
 [key: string]: V; 
}

let stringMap2: Map2<string> = {
 key: 'hello', 
}

제네릭 인터페이스 활용 예시

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: 'dobby',
 profile: {
  type: 'developer',
  skill: 'typescript',
 }
}

const studentUser: User<Student> = {
 name: 'sooyeon',
 profile: {
  type: 'student',
  school: '전남대학교',
 }
}

제네릭 클래스

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([1, 2, 3]);
numberList.pop();
numberList.push(4);
numberList.print();

const stringList = new List(['1', '2']);
stringList.push('hello');

프로미스(Promise)

자동으로 타입을 추론하는 기능을 가지고 있지 않다.
resolve의 타입은 정의할 수 있지만, reject에 대한 타입은 정의할 수 없다.

const promise = new Promise<number>((resolve, reject) => {
  setTimeout(() => {
    resolve(20); // number;
    reject('~~ 때문에 실패');
  }, 3000)
});

promise.then((response) => {
  console.log(response * 10); // number
})

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: '', content: ''});
   }, 3000);
 }); 
}

const postRequest = fetchPost();
postRequest.then((post) => {
  post.id;
})
profile
성장통을 겪고 있습니다.

0개의 댓글