Ch 02. JavaScript vs TypeScript
같은 코드를 두 언어로 나란히 작성해봅니다. 차이를 읽으면서 TypeScript가 무엇을 추가하는지 파악합니다.
변수와 타입 추론
JavaScript는 런타임에 타입을 결정합니다. TypeScript는 컴파일 시점에 타입을 확인합니다.
// JavaScriptlet count = 0;count = "hello"; // 아무 오류 없음count.toFixed(2); // 런타임 오류: count.toFixed is not a function
// TypeScriptlet count = 0; // 타입 추론: numbercount = "hello"; // 오류: Type 'string' is not assignable to type 'number'count.toFixed(2); // 안전하게 사용 가능
TypeScript는 초깃값을 보고 타입을 추론합니다. count = 0이라 쓰면 number로 확정되어, 이후 다른 타입의 값을 넣으려 하면 컴파일 오류가 납니다.
함수 시그니처
JavaScript 함수는 어떤 인자든 받을 수 있습니다.
// JavaScriptfunction formatPrice(price, currency) { return `${currency}${price.toFixed(2)}`;}formatPrice("free", "USD"); // 런타임 오류: price.toFixed is not a functionformatPrice(9.99); // NaN — currency가 undefined
TypeScript에서는 함수의 계약을 명확하게 정의합니다.
// TypeScriptfunction formatPrice(price: number, currency: string): string { return `${currency}${price.toFixed(2)}`;}formatPrice("free", "USD"); // 오류: Argument of type 'string' is not assignable to parameter of type 'number'formatPrice(9.99); // 오류: Expected 2 arguments, but got 1formatPrice(9.99, "USD"); // 정상: "$9.99"
객체와 인터페이스
JavaScript에서 객체는 형태가 자유롭습니다.
// JavaScriptfunction getUserFullName(user) { return `${user.firstName} ${user.lastName}`;}getUserFullName({ firstName: "Alice" }); // "Alice undefined"getUserFullName({ name: "Alice Smith" }); // "undefined undefined"getUserFullName(null); // 런타임 오류
TypeScript에서는 객체 형태를 인터페이스로 정의합니다.
// TypeScriptinterface User { firstName: string; lastName: string; email?: string; // ? 는 선택적 필드}function getUserFullName(user: User): string { return `${user.firstName} ${user.lastName}`;}getUserFullName({ firstName: "Alice" });// 오류: Property 'lastName' is missing in type '{ firstName: string; }' but required in type 'User'getUserFullName({ name: "Alice Smith" });// 오류: Object literal may only specify known properties, and 'name' does not exist in type 'User'getUserFullName(null);// 오류: Argument of type 'null' is not assignable to parameter of type 'User'getUserFullName({ firstName: "Alice", lastName: "Smith" }); // 정상
실제 버그 사례 비교
실무에서 자주 등장하는 패턴입니다. API 응답을 처리하는 코드입니다.
// JavaScript — 버그가 숨어있는 코드async function loadUserProfile(userId) { const response = await fetch(`/api/users/${userId}`); const data = await response.json(); // data.user 가 없는 경우? data.profile 인 경우? // 잘못된 키로 접근해도 컴파일러는 조용 return { name: data.user.name, avatar: data.user.avatarUrl, // avatarUrl? avatar_url? joinedAt: data.user.createdAt, };}
TypeScript에서는 API 응답 타입을 정의하고, 형태가 맞지 않으면 오류를 냅니다.
// TypeScript — 타입 안전한 코드interface ApiUser { id: number; name: string; avatar_url: string; created_at: string;}interface ApiResponse { user: ApiUser;}async function loadUserProfile(userId: number): Promise<{ name: string; avatar: string; joinedAt: string;}> { const response = await fetch(`/api/users/${userId}`); const data: ApiResponse = await response.json(); return { name: data.user.name, avatar: data.user.avatar_url, joinedAt: data.user.created_at, };}// 아래 호출은 컴파일 시 바로 오류 발생loadUserProfile("not-a-number"); // 오류: Argument of type 'string' is not assignable to parameter of type 'number'
null과 undefined 처리
JavaScript에서 null은 유명한 버그의 원인입니다.
// JavaScriptfunction getFirstItem(arr) { return arr[0].name; // arr가 빈 배열이면? arr[0]이 없으면?}getFirstItem([]); // 런타임 오류: Cannot read properties of undefined
TypeScript의 strict 모드는 null 가능성을 컴파일 시점에 강제합니다.
// TypeScript — strict 모드interface Item { name: string;}function getFirstItem(arr: Item[]): string | undefined { return arr[0]?.name; // 옵셔널 체이닝으로 안전하게 접근}const result = getFirstItem([]);// result 를 바로 사용하려 하면 오류console.log(result.toUpperCase());// 오류: Object is possibly 'undefined'// null 체크를 하면 통과if (result !== undefined) { console.log(result.toUpperCase()); // 정상}
TypeScript는 JavaScript의 상위 집합
중요한 사실 하나입니다. 모든 유효한 JavaScript 코드는 TypeScript 코드이기도 합니다.
// 이 코드는 TypeScript 파일(.ts)에서도 완벽히 유효const names = ["Alice", "Bob", "Carol"];names.forEach(name => console.log(name));
TypeScript는 JavaScript에 타입 문법을 얹은 것입니다. 컴파일하면 타입 정보가 제거된 순수 JavaScript가 나옵니다.
// 입력: TypeScriptfunction greet(name: string): string { return `Hello, ${name}!`;}
// 출력: JavaScript (컴파일 후)function greet(name) { return `Hello, ${name}!`;}
브라우저와 Node.js는 TypeScript를 직접 실행하지 않습니다. tsc 컴파일러가 JavaScript로 변환해줍니다. 다음 PART에서 직접 설치하고 확인합니다.
정리
| 항목 | JavaScript | TypeScript |
|---|---|---|
| 타입 확인 시점 | 런타임 | 컴파일 타임 |
| 오류 발견 시점 | 실행 후 | 코드 작성 중 |
| 객체 구조 강제 | 없음 | 인터페이스로 정의 |
| null 안전성 | 개발자 책임 | strict 모드로 강제 |
| 브라우저 실행 | 직접 가능 | 컴파일 후 실행 |
TypeScript는 JavaScript를 대체하는 게 아닙니다. 더 안전하게 JavaScript를 작성할 수 있도록 돕는 도구입니다.