※ 이 글은 이정환 강사님의 '한 입 크기로 잘라먹는 타입스크립트' 강의를 본 뒤 정리한 글입니다.
제네릭(Generic) 함수
- 함수나 인터페이스, 타입 별칭, 클래스 등을 다양한 타입과 함께 동작하도록 만들어주는 TS 기능
- 두루두루 모든 타입의 값을 다 적용할 수 있는 범용적인 함수
- 형태: 함수이름<T> (value:T):T
※ 어떨 때 필요할까?
[예시] 다양한 타입의 매개변수를 받고 해당 매개변수를 그대로 반환하는 함수가 필요한 상황
function func(value: any){
return value;
}
let num = func(10)
let str = func("string")
num.toUpperCase() // 오류이나 감지하지 못함
- 위의 함수의 경우, 어떤 값을 넣어도 any 타입으로 반환되기 때문에 잘못된 메서드를 적용해도 TS가 오류를 감지하지 못함 -> 나중에 런타임 오류 발생.
[해결책] 제네릭 함수 사용
function func<T>(value:T): T {
return value;
}
let num = func(10) // number 타입으로 출력

- T에 어떤 타입이 할당되는지는 함수가 호출될 때 결정됨.
- 제네릭 함수 호출 시, 타입변수에 할당할 타입을 직접 명시하는 것도 가능.
function func<T>(value: T): T {
return value;
}
// 제네릭 함수 호출
let arr = func<[number, number, number]>([1,2,3]);
- 2개 이상의 타입변수도 사용 가능
function swap<T, U>(a: T, b: U){
return [b,a]
}
const [a,b] = swap("1",2);
// T : string 타입, U는 number 타입으로 추론
제네릭 함수 응용
1. Map 메서트 타입 정의하기
- map(): 원본 배열의 각 요소에 콜백함수를 수행하여 반환된 값을 새로운 배열로 반환하는 메소드.
- 형태: arr.map((it) => it*2)
function map<T>(
arr: T[], callback: ((item: T) => T) : T[]
){
let result = [];
for(let i = 0; i < arr.length; i++){
result.push(callback[arr[i]);
}
return result;
}
map(arr, (i) => i*2); // [2,4,6]
2. ForEach 메서드 타입 정의하기
- forEach(): 배열의 모든 요소에 콜백함수를 한 번씩 수행하는 메소드.
function forEach<T>(arr: T[], callback: (item: T) => void){
for(let i = 0; i < arr.length; i++){
callback(arr[i]);
}
}
제네릭 인터페이스 / 타입별칭 / 클래스
- 제네릭은 인터페이스, 타입별칭, 클래스에도 적용할 수 있다!
제네릭 인터페이스
// 인터페이스 정의
interface KeyPair<K,V>{
key: K;
value: V;
}
// 사용
let keyPair: KeyPair<string, number> = {
key: "key",
value: 0
}
let keyPair2: KeyPair<boolen, string[]> = {
key: true,
value: ["1"]
}
- 인터페이스에 제네릭을 쓸 때는, 변수타입 정의시 반드시 꺽쇠와 함께 타입 변수에 할당할 타입을 명시해야 한다.
- 함수는 매개변수의 값을 기준으로 타입변수의 값을 추론할 수 있지만 인터페이스는 추론할 값이 없기 때문!
- 인터페이스와 인덱스 시그니처를 함께 사용하면 더 유연한 객체타입 정의가 가능함.
interface Map<V>{
// 인덱스 시그니처 - [key: type] : value 요 형태!
[key: string]: V;
}
let stringMap: Map<string> = {
key: "value"
}
let boolenMap: Map<boolean> = {
key: true;
}
제네릭 타입별칭
- 인터페이스와 똑같이 타입별칭에도 제네릭을 적용할 수 있다!
- 인터페이스와 마찬가지로, 반드시 타입 변수에 설정할 타입을 명시해야함.
type Map<V> = {
[key: string]: V
}
let stringMap2: Map2<string> = {
key: "안녕하세요";
}
제네릭 클래스
- 제네릭을 이용해 범용적으로 사용 가능한 클래스를 만들 수 있다!(타입별로 따로따로 만들 필요 X)
class List<T> {
constructor(private list: T[]){}
push(data: T){
this.list.push(data)
}
pop(){
return this.list.pop(data)
}
print(){
console.log(this.list)
}
}
const numberList = new List([1,2,3]);
const stringList = new List(["1","2"])
프로미스와 제네릭
※ 프로미스란?
- 자바스크립트에서 비동기 작업을 처리할 때 사용하는 객체.
- '비동기 처리'란 현재 실행하는 작업과 별도로 다른 작업을 수행하는 것. A 작업이 끝나면 B를 처리하라고 명령하기 위해 전통적으로는 callback 함수를 썼으나 작업량이 많아질수록 코드가 깊어지는 '콜백 지옥' 현상이 발생해 가독성을 떨어뜨림. => 프로미스의 등장
- 비동기 작업이 끝날때까지 기다리는게 아니라, 결과를 제공하겠다는 '약속'을 반환한다는 의미에서 Promise라고 명명되었다 함.
- resolve, reject / .then(), .catch() 메서드 체이닝을 이용하여 성공과 실패에 대한 후속 처리를 진행.
// 프로미스 객체를 반환하는 함수 생성
const myPromise = new Promise((resolve, reject) => {
// 비동기 작업 수행
const data = fetch('서버로부터 요청할 때');
if(data) {
resolve(data) // 성공시 실행
} eles {
reject("error!") // 실패시 실행
}
})
// 프로미스 객체를 반환하는 함수 사용
myPromise()
.then((result) => {
console.log(`성공! 데이터: ${result}`)
})
.catch((err) => {
console.log(`실패! 에러메세지: ${err}`)
})
프로미스 객체를 함수로 만드는 이유(프로미스 팩토리 함수)
- 재사용성: 필요할 때 마다 호출하여 사용 가능. 반복되는 비동기 작업을 효율적으로 처리할 수 있다.
- 가독성: 코드의 가독성이 높아진다.
- 확장성: 프로미스 객체를 함수로 만들면 인자를 전달하며 동적으로 비동기 작업을 수행할 수 있다. 또한, 여러개의 프로미스 객체 반환 함수를 연결해 복잡한 비동기 로직을 구현할 수 있다.
프로미스의 상태
- Pending(대기)
- 아직 비동기 처리 로직이 완료되지 않은 상태
- Fulfilled(이행)
- 비동기 처리 로직이 성공적으로 완료된 상태
- resolve()의 결과
- 체이닝된 .then() 메서드를 호출해 처리결과 값을 받을 수 있다
- Rejected(거부)
- 비동기 처리 로직이 실패한 상태
- reject()의 결과
- 체이닝된 .catch() 메서드를 호출해 실패에 대한 행동을 수행
프로미스 핸들러
- 프로미스의 성공/실패 여부에 따라 실행되는 콜백 함수
- then(), catch(), finally()
- then()을 반복해 사용하면 프로미스 지옥에 빠질 수 있음
=> 이를 극복하기 위해 나온 것: async() / await! << 추후 정리
제네릭에서 프로미스 사용하기
- promise는 제네릭 클래스로 구현되어 있음
- Promise를 생성할 때 타입 변수에 할당할 타입을 직접 설정해주면 그 타입이 resolve 결과값의 타입이 됨.
- reject 값의 타입은 정의할 수 없음. 그냥 unknown 타입으로 고정 => 타입좁히기 사용하는게 안전
const promise = new Promise<number>(() => {
setTimeout(() => {
resolve(20);
}, 3000)
})
promise
.then((res)=> {
console.log(res) // res는 number 타입
})
.catch((err) => {
if(typeof err === "string"){
console.log(err)
}
})'개발공부' 카테고리의 다른 글
| [공식문서 읽기/API] LocalStorage / Geolocation (0) | 2025.03.17 |
|---|---|
| #8. 타입 조작하기 (0) | 2025.03.05 |
| #6. 클래스 (0) | 2025.02.27 |
| #5. 인터페이스 (0) | 2025.02.26 |
| #4. 함수와 타입 (0) | 2025.02.25 |