
📅 2025-11-27
➡️ TypeScript 제네릭, 유틸리티 타입에 대해 새롭게 알게 된 것 또는 헷갈리는 부분 정리
any 대신 타입 정보를 유지하기 위해
any 타입function logText(text: any): any {
return text;
}
const result = logText("Hello");
any를 사용하면 어떤 값이 들어올지 알 수 없고any라 자동완성과 타입체크 불가능→ 타입스크립트의 장점이 사라짐
function logText<T>(text: T): T {
return text;
}
const r1 = logText<string>("Hello"); // 타입 명시
const r2 = logText("Hello"); // 타입 추론
T)을 그대로 반환 타입에도 반영→ 타입 안전성 + 유연함 유지
유니온 타입보다 더 일관된 재사용하기 위해
function toArray(a: number | string, b: number | string): (number | string)[] {
return [a, b];
}
toArray(1, 2); // 숫자만
toArray("1", "2"); // 문자열만
toArray(1, "2"); // 숫자 + 문자열 섞임
function toArray<T>(a: T, b: T): T[] {
return [a, b];
}
toArray<number>(1, 2); // T = number → number[]
toArray<string>("1", "2"); // T = string → string[]
toArray<string | number>(1, "2"); // T = string | number → (string | number)[]
toArray<number>(1, "2"); // Error
→ 일관성 있는 타입 보장
toArray(1, 2); // T = number
toArray("1", "2"); // T = string
toArray(1, "2"); // Errorclass GenericMath<T> {
pi!: T;
sum!: (x: T, y: T) => T;
}
const math = new GenericMath<number>();
math.pi = 3.14;
math.sum = (x, y) => x + y;
extends 사용interface MyType<T extends string | number> {
name: string;
value: T;
}
const dataA: MyType<string> = {
name: "Data A",
value: "Hello world",
};
const dataB: MyType<number> = {
name: "Data B",
value: 1234,
};
const dataC: MyType<boolean> = {
// Error: boolean은 string | number에 해당하지 않음
name: "Data C",
value: true,
};T는 반드시 string 또는 number여야 함type Primitive = string | number | boolean;
type MyType2<T extends Primitive> = {
name: string;
value: T;
};function logLength<T extends { length: number }>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("Hello"); // 5
logLength([1, 2, 3]); // 3T는 반드시 length 속성을 가지고 있어야 함function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let obj = { a: 1, b: 2, c: 3 };
getProperty(obj, "a"); // OK
getProperty(obj, "c"); // OK
getProperty(obj, "z"); // Error: "z"는 "a" | "b" | "c"에 포함되지 않음
keyof T : T 타입의 모든 키들의 유니온 타입 ("a" | "b" | "c" 등)K extends keyof T : K는 반드시 T의 키 중 하나기존 타입을 변형해 새로운 타입을 쉽게 만들 수 있음
중복된 타입 정의를 줄일 수 있음
타입스크립트 내장 유틸리티 타입의 기반 기능 (Readonly, Partial, Required, Record 등)
예시: Readonly 타입
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Person = {
name: string;
age: number;
};
type ReadonlyPerson = Readonly<Person>;
결과
{
readonly name: string;
readonly age: number;
}
[P in keyof T]: T의 모든 키를 하나씩 순회(name, age 등)readonly: 각 속성을 읽기 전용으로 변경T[P]: 기존 속성 타입 그대로 사용예시: Optional 타입
type Optional<T> = {
[P in keyof T]?: T[P];
};
type Person = {
name: string;
age: number;
};
type OptionalPerson = Optional<Person>
결과
{
name?: string;
age?: number;
}
?가 붙어 모든 속성이 선택적(optional)이 됨function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const mergedObj = merge({ name: "Alice" }, { age: 25 });
console.log(mergedObj);
// { name: "Alice", age: 25 }<T, U>: 두 종류의 타입 변수를 받음obj1: T, obj2: U: 서로 다른 타입을 허용T & U: 인터섹션 타입, 즉 두 타입을 합친 타입useStateimport { useState } from "react";
function App() {
const [counter, setCounter] = useState<number>(1);
const increment = () => {
setCounter((prev) => prev++);
};
return <div onClick={increment}>{counter}</div>;
}
export default App; 이렇게 useState 뒤에 <> , number타입을 넣으면 counter, setCounter에 각각 number를 인자로 받는 함수로 받는 함수 타입이 할당됨🔗 https://www.typescriptlang.org/docs/handbook/utility-types.html
T에서 특정 속성 K만 선택해 새로운 타입 생성interface Todo {
id: number;
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};Pick과 반대T에서 특정 속성 K만 제외하고 새로운 타입 생성interface Todo {
id: number;
title: string;
description: string;
completed: boolean;
}
type TodoWithoutDesc = Omit<Todo, "description">;
const todo: TodoWithoutDesc = {
id: 1,
title: "Clean room",
completed: false,
};T에서 U를 제거type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>;
// string | numberExclude와 반대type E0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type E1 = Extract<string | number | (() => void), Function>; // () => voidT의 모든 속성을 선택적(?)으로 변경interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
const todo1: Todo = {
title: "organize desk",
description: "clear clutter",
};
const todo2 = updateTodo(todo1, {
description: "throw out trash",
});
console.log(todo1); // { title: 'organize desk', description: 'clear clutter' }
console.log(todo2); // { title: 'organize desk', description: 'throw out trash' }T의 모든 속성을 읽기 전용으로 변경(readonly)interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: "Delete inactive users",
};
todo.title = "Hello"; // Error: 읽기 전용 프로퍼티에 재할당할 수 없습니다.K에 대해 모든 값이 타입 T를 가지는 객체 생성interface PageInfo {
title: string;
}
type Page = "home" | "about" | "contact";
const pages: Record<Page, PageInfo> = {
home: { title: "home" },
about: { title: "about" },
contact: { title: "contact" },
};T의 반환 타입 추론function getUser() {
return { name: "Alice", age: 25 };
}
type User = ReturnType<typeof getUser>;
const user: User = { name: "Alice", age: 25 };T의 매개변수 타입들을 튜플로 만들어 줌function log(message: string, userId: number): void {
console.log(`${userId}: ${message}`);
}
type LogParams = Parameters<typeof log>; // [string, number]
const params: LogParams = ["Hello, world!", 1];
log(...params); // 1: Hello, world!async function fetchData(): Promise<string> {
return "Hello, world!";
}
type FetchDataType = Awaited<ReturnType<typeof fetchData>>;
const data: FetchDataType = await fetchData(); // data: stringasync function exampleFunction(): Promise<number> {
return 42;
}
type ExampleReturn = ReturnType<typeof exampleFunction>; // Promise<number>
type ExampleResolved = Awaited<ExampleReturn>; // number
ReturnType으로 함수 반환 타입을 가져오고Awaited로 실제 값 타입까지 뽑아낼 수 있음| 유틸리티 타입 | 역할 |
|---|---|
Pick | 필요한 속성만 선택 |
Omit | 특정 속성 제외 |
Exclude | 유니온에서 제거 |
Extract | 유니온에서 추출 |
Partial | 모든 속성 optional |
Readonly | 모든 속성 readonly |
Record | 키-값 맵 타입 생성 |
ReturnType | 함수 반환 타입 추론 |
Parameters | 매개변수 타입 튜플 |
Awaited | Promise 결과 타입 |
type Todo = {
id: number;
title: string;
completed: boolean;
};
type TodoId = Pick<Todo, "id">;
type CreateTodo = Pick<Todo, "title" | "completed">;
// type CreateTodo = Omit<Todo, "id">;
type ToggleTodo = Pick<Todo, "id" | "completed">;
// type ToggleTodo = Omit<Todo, "title">;
type EditTodo = Partial<Todo> & TodoId;