TypeScript
TypeScript Record 타입 제대로 사용하기
Record 타입을 모든 키를 필수로 만들지 않으면 런타임 에러가 난다. optional 처리를 정확히 해야 한다.
API 응답에서 사용자 ID별 권한을 매핑할 때 Record를 썼는데 배포 후 undefined 에러가 났다. 모든 사용자 ID가 응답에 포함된다고 가정했기 때문이다.
Record 타입의 기본
Record 타입은 특정 키와 값의 타입을 정의한다:
type Permissions = Record<string, boolean>;
// 이렇게 쓸 수 있다
const userPerms: Permissions = {
user1: true,
user2: false
};
하지만 이렇게 하면 모든 키가 있다고 가정한다. 없는 키로 접근하면 undefined가 나온다.
Optional 처리가 필요한 경우
데이터베이스나 API에서 온 데이터는 완전하지 않을 수 있다:
type UserPermissions = Record<string, boolean | undefined>;
const perms: UserPermissions = {};
console.log(perms.user1); // undefined (안전함)
아니면 Partial을 사용하면 모든 값이 optional이 된다:
type UserPermissions = Partial<Record<string, boolean>>;
const perms: UserPermissions = {};
console.log(perms.user1); // undefined (타입 안전)
타입 좁히기
존재하지 않는 키에 접근하는 걸 방지하려면:
function checkPermission(userId: string, perms: Record<string, boolean>): boolean {
if (!(userId in perms)) {
return false; // 없으면 false
}
return perms[userId];
}
또는 optional chaining과 nullish coalescing:
const hasPermission = perms[userId] ?? false;
컴파일 시 확인
npx tsc --noEmit
TypeScript 컴파일 단계에서 타입 에러를 모두 찾아야 한다. 런타임에 undefined로 터지는 건 방지할 수 있다.
실제 API 응답 다루기
API에서 온 데이터는 항상 불완전할 수 있다고 가정하자:
interface ApiResponse {
users?: Record<string, { name: string; active: boolean }>;
}
function processUsers(response: ApiResponse) {
const users = response.users ?? {};
// users는 빈 객체일 수도 있고 몇몇 키만 있을 수도 있다
}
타입 좁히기의 중요성
런타임에 존재하지 않는 키에 접근하는 걸 피하는 게 핵심이다. TypeScript가 컴파일 단계에서 잡을 수 없는 부분들도 많으니, 런타임에 안전하게 처리하는 습관을 들이자.
작은 타입 체크가 배포 후 undefined 관련 버그를 많이 줄인다.