TypeScript는 Microsoft가 2012년 개발한 JavaScript의 정적 타입 슈퍼셋이다. JavaScript에 타입 시스템을 추가해 컴파일 시점에 오류를 잡고, 대규모 코드베이스의 유지보수성을 크게 향상시킨다. 컴파일하면 순수 JavaScript로 변환된다.
JavaScript와의 관계
TypeScript (.ts)
↓ tsc (TypeScript Compiler)
JavaScript (.js)
↓
모든 JS 실행 환경 (브라우저, Node.js, Deno)
모든 JavaScript 코드는 TypeScript 코드이기도 하다 (하위 호환).
타입 시스템
typescript
// 기본 타입
let name: string = "Alice";
let age: number = 30;
let active: boolean = true;
let scores: number[] = [90, 85, 92];
let tuple: [string, number] = ["Alice", 30];
// 객체 타입
interface User {
id: number;
name: string;
email?: string; // 선택적 속성
}
const user: User = { id: 1, name: "Alice" };
// 함수 타입
function add(a: number, b: number): number {
return a + b;
}
// 제네릭
function identity<T>(arg: T): T {
return arg;
}
identity<string>("hello");
identity<number>(42);
유니온 & 인터섹션 타입
typescript
// 유니온 타입: A 또는 B
type ID = string | number;
let userId: ID = "abc";
userId = 123; // 둘 다 가능
// 인터섹션 타입: A이면서 B
type AdminUser = User & { permissions: string[] };
// 리터럴 타입
type Direction = "north" | "south" | "east" | "west";
let dir: Direction = "north";
// dir = "up"; // 컴파일 오류!
타입 가드
typescript
function processInput(input: string | number) {
if (typeof input === "string") {
return input.toUpperCase(); // string 메서드 안전하게 사용
}
return input * 2; // number 연산 안전하게 사용
}
// instanceof 가드
class Dog { bark() {} }
class Cat { meow() {} }
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) animal.bark();
else animal.meow();
}
// 사용자 정의 타입 가드
function isUser(obj: any): obj is User {
return typeof obj.id === "number" && typeof obj.name === "string";
}
제네릭 (Generics)
typescript
// 재사용 가능한 타입-안전 컨테이너
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const numStack = new Stack<number>();
numStack.push(1);
numStack.push(2);
numStack.pop(); // 2
// 제네릭 제약
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: "Alice" };
getProperty(user, "name"); // ✓
// getProperty(user, "age"); // ✗ 컴파일 오류
유틸리티 타입
typescript
interface User {
id: number;
name: string;
email: string;
}
// 모든 필드를 선택적으로
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }
// 특정 필드만 선택
type UserPreview = Pick<User, "id" | "name">;
// 특정 필드 제외
type UserWithoutId = Omit<User, "id">;
// 읽기 전용
type ReadonlyUser = Readonly<User>;
// Record: 키-값 맵 타입
type RoleMap = Record<"admin" | "user" | "guest", string[]>;
// ReturnType: 함수 반환 타입 추출
function getUser() { return { id: 1, name: "Alice" }; }
type GetUserReturn = ReturnType<typeof getUser>; // { id: number; name: string }
// NonNullable: null/undefined 제거
type SafeId = NonNullable<string | null | undefined>; // string
매핑 타입 (Mapped Types)
기존 타입의 각 프로퍼티를 변환해 새 타입을 생성.
typescript
// 모든 프로퍼티를 선택적으로 (Partial 구현 원리)
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
// 모든 프로퍼티를 nullable로
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
// 읽기 전용 + 선택적으로
type OptionalReadonly<T> = {
readonly [K in keyof T]?: T[K];
};
// 실용 예시: API 응답 타입 변환
type ApiResponse<T> = {
[K in keyof T]: T[K] extends string ? T[K] | null : T[K];
};
조건부 타입 (Conditional Types)
typescript
// T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// infer로 타입 추출
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type Resolved = UnpackPromise<Promise<string>>; // string
type NotPromise = UnpackPromise<number>; // number
// 배열 원소 타입 추출
type ElementType<T> = T extends (infer U)[] ? U : never;
type StrElem = ElementType<string[]>; // string
템플릿 리터럴 타입
typescript
type EventName = "click" | "focus" | "blur";
type EventHandler = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"
type HttpMethod = "get" | "post" | "put" | "delete";
type ApiRoute = `/${string}`;
type ApiEndpoint = `${Uppercase<HttpMethod>} ${ApiRoute}`;
// "GET /..." | "POST /..." | ...
// 실용 예시: 객체 키 변환
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
type UserGetters = Getters<User>;
// { getId: () => number; getName: () => string; getEmail: () => string }
satisfies 연산자 (TypeScript 4.9+)
타입 검사는 하되 추론 결과는 좁은 타입 유지.
typescript
type Colors = "red" | "green" | "blue";
type Palette = Record<Colors, string | [number, number, number]>;
const myPalette = {
red: "#ff0000",
green: [0, 255, 0],
blue: "#0000ff",
} satisfies Palette;
// satisfies 덕분에 타입이 좁게 유지됨
myPalette.green.map(v => v * 2); // ✓ (배열로 인식)
// Palette로 선언했다면: string | number[] → 배열 메서드 사용 불가
JavaScript와의 비교
| 항목 | JavaScript | TypeScript |
|---|
| 타입 검사 | 런타임 | 컴파일 타임 |
| 오류 발견 | 실행 중 | 코딩 중 |
| IDE 지원 | 기본 | 강력한 자동완성 |
| 학습 곡선 | 낮음 | 중간 |
| 대규모 프로젝트 | 어려움 | 용이 |
TypeScript 5.x 주요 업데이트
| 버전 | 연도 | 주요 기능 |
|---|
| 5.0 | 2023 | ECMAScript 데코레이터 표준화, const 타입 매개변수 |
| 5.2 | 2023 | using 선언(명시적 리소스 관리), 데코레이터 메타데이터 |
| 5.4 | 2024 | NoInfer 유틸리티 타입, 클로저 타입 좁히기 개선 |
| 5.5 | 2024 | 타입 단언 자동 추론 (.filter() 등), 정규식 구문 검사 |
| 5.8 | 2025 | Node.js 22 ESM require() 지원 |
using 선언 (TypeScript 5.2+)
function doWork() {
using file = getFileHandle(); // 스코프 종료 시 file.dispose() 자동 호출
// ... 파일 사용
} // try...finally 없이도 안전하게 정리
관련 개념
- •JavaScript — TypeScript의 기반 언어
- •객체지향 프로그래밍 — TypeScript의 클래스/인터페이스
- •컴파일 — .ts → .js 변환 과정
참고문헌
- •TypeScript 공식 문서: typescriptlang.org
- •Vanderkam, D. (2021). Effective TypeScript