1.4 Hello TypeScript
환경 설정이 완료되었다. 이제 실제로 TypeScript 코드를 작성하고, 컴파일하고, 실행해 보자. 이 장에서는 첫 번째 .ts 파일을 작성하는 것부터 시작해서 타입 오류를 의도적으로 만들고 에러 메시지를 읽는 법, 그리고 TypeScript의 핵심 특성인 타입 추론까지 다룬다.
첫 .ts 파일 작성
src/hello.ts 파일을 만들고 다음 코드를 작성한다.
// src/hello.ts
const greeting: string = "Hello, TypeScript!";
const version: number = 5;
const isReady: boolean = true;
function sayHello(name: string, language: string): string {
return `${name}에서 ${language}를 시작합니다!`;
}
const message: string = sayHello("한국", "TypeScript");
console.log(greeting);
console.log(`버전: ${version}, 준비 완료: ${isReady}`);
console.log(message);
코드 분석
const greeting: string— 변수 뒤에: 타입을 붙이는 것을 **타입 어노테이션(Type Annotation)**이라 한다.greeting은 문자열만 담을 수 있다.const version: number— 숫자 타입. 정수와 실수를 구분하지 않는다.const isReady: boolean— 참/거짓 타입.function sayHello(name: string, language: string): string— 매개변수 타입과 반환 타입을 명시했다. 마지막: string이 반환 타입이다.
tsc로 컴파일하기
단일 파일 컴파일
npx tsc src/hello.ts
같은 디렉토리에 src/hello.js가 생성된다.
생성된 JavaScript 파일 확인
// src/hello.js — 타입 어노테이션이 모두 제거되었다
"use strict";
const greeting = "Hello, TypeScript!";
const version = 5;
const isReady = true;
function sayHello(name, language) {
return `${name}에서 ${language}를 시작합니다!`;
}
const message = sayHello("한국", "TypeScript");
console.log(greeting);
console.log(`버전: ${version}, 준비 완료: ${isReady}`);
console.log(message);
타입 어노테이션이 완전히 사라졌다. 함수 매개변수의 : string, 변수의 : string, : number, : boolean, 반환 타입 : string이 모두 제거되었다. 남은 것은 순수한 JavaScript다.
프로젝트 전체 컴파일 (tsconfig.json 사용)
tsconfig.json에 rootDir과 outDir을 설정했다면 다음 명령으로 프로젝트 전체를 컴파일한다.
npx tsc
src/hello.ts → dist/hello.js로 컴파일된다.
실행
node dist/hello.js
# Hello, TypeScript!
# 버전: 5, 준비 완료: true
# 한국에서 TypeScript를 시작합니다!
타입 에러 의도적으로 발생시키기
TypeScript의 진가는 잘못된 코드를 실행하기 전에 잡아내는 것이다. 일부러 타입 오류를 만들어 에러 메시지를 읽는 법을 익혀 보자.
에러 예제 1: 잘못된 타입의 값 전달
// src/error-test.ts
function multiply(a: number, b: number): number {
return a * b;
}
const result = multiply(10, "5");
// ^^^
// 오류 TS2345: Argument of type 'string' is not assignable
// to parameter of type 'number'.
에러 메시지 읽는 법:
TS2345— TypeScript 에러 코드. 이 코드를 검색하면 상세 설명을 찾을 수 있다.Argument of type 'string'— 전달한 값의 타입is not assignable to parameter of type 'number'— 기대하는 타입- 즉, "문자열을 숫자 매개변수에 전달할 수 없다"는 뜻이다.
에러 예제 2: 존재하지 않는 속성 접근
const person = {
name: "Alice",
age: 30,
};
console.log(person.email);
// ^^^^^
// 오류 TS2339: Property 'email' does not exist on type
// '{ name: string; age: number; }'.
에러 메시지 읽는 법:
TS2339— 속성이 존재하지 않을 때 나오는 에러 코드Property 'email' does not exist on type '...'—person객체에email속성이 없다
에러 예제 3: 반환 타입 불일치
function getAge(): number {
return "서른";
// ^^^^^^
// 오류 TS2322: Type 'string' is not assignable to type 'number'.
}
에러 메시지 읽는 법:
TS2322— 타입 불일치 오류. 가장 자주 만나는 에러 코드 중 하나다.- 선언한 반환 타입(
number)과 실제 반환값의 타입(string)이 다르다.
자주 만나는 TypeScript 에러 코드
| 에러 코드 | 의미 | 예시 상황 |
|---|---|---|
| TS2322 | 타입 불일치 | const x: number = "hello" |
| TS2339 | 존재하지 않는 속성 | obj.nonExistent |
| TS2345 | 인수 타입 불일치 | fn("str") (number 기대) |
| TS2304 | 이름을 찾을 수 없음 | 선언하지 않은 변수 사용 |
| TS2531 | null일 수 있는 객체 | null일 수 있는 값에 접근 |
| TS7006 | 암묵적 any 타입 | 타입 없는 매개변수 (strict 모드) |
TypeScript Playground
코드 에디터 없이 브라우저에서 바로 TypeScript를 실행해볼 수 있는 공식 도구다.
주소: https://www.typescriptlang.org/play
Playground에서 할 수 있는 것들:
- TypeScript 코드 작성 및 즉시 컴파일 결과 확인
- 왼쪽에 TypeScript, 오른쪽에 컴파일된 JavaScript가 실시간으로 표시된다
- 타입 오류 즉시 표시
- 공유 URL 생성 — 코드를 URL로 공유해 팀원과 문제를 논의할 수 있다
Playground 활용 팁
상단 탭에 여러 보기 옵션이 있다:
.JS탭: 컴파일된 JavaScript 결과DTS탭: 자동 생성되는 타입 선언 파일 (.d.ts) 미리보기Logs탭:console.log출력 결과
Playground URL에 TypeScript 버전도 선택할 수 있어 버전별 동작 차이를 비교하기 좋다.
타입 명시 vs 타입 추론
TypeScript는 대부분의 경우 값으로부터 타입을 자동으로 **추론(Inference)**한다. 모든 곳에 타입을 명시할 필요가 없다.
타입 추론이 작동하는 경우
// 초기값으로 타입을 추론한다 — 타입 명시 불필요
const name = "Alice"; // TypeScript가 string으로 추론
const age = 30; // TypeScript가 number로 추론
const isActive = true; // TypeScript가 boolean으로 추론
// 타입을 명시해도 동일하지만 중복이다
const name2: string = "Bob"; // 불필요한 타입 명시
// 반환 타입도 추론 가능
function add(a: number, b: number) {
return a + b; // 반환 타입이 number로 추론됨
}
타입을 명시해야 하는 경우
// 1. 초기값이 없는 변수
let userInput: string; // 명시 필요
userInput = "hello";
// 2. 추론과 다른 타입이 필요한 경우
const numbers: number[] = []; // [] 만으로는 never[]로 추론된다
// 3. 함수 매개변수 — 항상 명시
function processData(data: string): void {
console.log(data);
}
// 4. 복잡한 객체 타입
interface Config {
host: string;
port: number;
ssl: boolean;
}
const config: Config = {
host: "localhost",
port: 3000,
ssl: false,
};
언제 명시하고 언제 생략하는가
원칙은 간단하다. TypeScript가 올바르게 추론할 수 있으면 생략하고, 추론이 모호하거나 의도와 다르면 명시한다.
// 생략해도 되는 경우 (추론이 명확)
const count = 0; // number
const title = "TypeScript"; // string
const items = [1, 2, 3]; // number[]
// 명시해야 하는 경우 (추론이 불명확하거나 의도와 다름)
const mixedArray: (string | number)[] = []; // 빈 배열
let value: string | null = null; // null로 시작
// 함수 매개변수는 항상 명시 (추론 불가)
function double(n: number): number {
return n * 2;
}
함수에 타입 추가하기
함수는 TypeScript에서 가장 많이 타입을 사용하는 곳이다.
기본 함수 타입
// 매개변수 타입과 반환 타입 명시
function add(a: number, b: number): number {
return a + b;
}
// 화살표 함수
const multiply = (a: number, b: number): number => a * b;
// 반환값이 없는 함수 (void)
function logMessage(message: string): void {
console.log(message);
// return 없음, 또는 return; 만 허용
}
// 절대 반환되지 않는 함수 (never)
function throwError(message: string): never {
throw new Error(message);
}
선택적 매개변수
// 물음표(?)를 붙이면 선택적 매개변수
function greet(name: string, greeting?: string): string {
if (greeting) {
return `${greeting}, ${name}!`;
}
return `안녕하세요, ${name}!`;
}
greet("Alice"); // "안녕하세요, Alice!"
greet("Alice", "Hello"); // "Hello, Alice!"
기본값 매개변수
function createUser(name: string, role: string = "user"): object {
return { name, role };
}
createUser("Alice"); // { name: "Alice", role: "user" }
createUser("Bob", "admin"); // { name: "Bob", role: "admin" }
실전 예제: 타입이 적용된 코드 작성
예제 1: 할인 계산기
// src/discount-calculator.ts
interface Product {
name: string;
price: number;
category: string;
}
interface DiscountResult {
originalPrice: number;
discountAmount: number;
finalPrice: number;
discountPercent: number;
}
function calculateDiscount(product: Product, discountPercent: number): DiscountResult {
if (discountPercent < 0 || discountPercent > 100) {
throw new Error("할인율은 0에서 100 사이여야 합니다.");
}
const discountAmount = product.price * (discountPercent / 100);
const finalPrice = product.price - discountAmount;
return {
originalPrice: product.price,
discountAmount,
finalPrice,
discountPercent,
};
}
const laptop: Product = {
name: "노트북",
price: 1200000,
category: "전자제품",
};
const result = calculateDiscount(laptop, 15);
console.log(`${result.discountPercent}% 할인 적용:`);
console.log(`원가: ${result.originalPrice.toLocaleString()}원`);
console.log(`할인액: ${result.discountAmount.toLocaleString()}원`);
console.log(`최종가: ${result.finalPrice.toLocaleString()}원`);
// 출력:
// 15% 할인 적용:
// 원가: 1,200,000원
// 할인액: 180,000원
// 최종가: 1,020,000원
예제 2: 사용자 인사 함수
// src/greeter.ts
type Language = "ko" | "en" | "ja";
interface User {
name: string;
preferredLanguage: Language;
isVip?: boolean;
}
function greetUser(user: User): string {
const greetings: Record<Language, string> = {
ko: "안녕하세요",
en: "Hello",
ja: "こんにちは",
};
const greeting = greetings[user.preferredLanguage];
const vipBadge = user.isVip ? " [VIP]" : "";
return `${greeting}, ${user.name}${vipBadge}님!`;
}
const koreanUser: User = {
name: "김민수",
preferredLanguage: "ko",
isVip: true,
};
const englishUser: User = {
name: "Alice",
preferredLanguage: "en",
};
console.log(greetUser(koreanUser)); // "안녕하세요, 김민수 [VIP]님!"
console.log(greetUser(englishUser)); // "Hello, Alice님!"
// 타입 오류 예시 — 존재하지 않는 언어는 컴파일타임에 차단됨
const invalidUser: User = {
name: "Test",
preferredLanguage: "fr", // 오류: '"fr"' is not assignable to type 'Language'
};
고수 팁
Playground의 Declaration 탭 활용
TypeScript Playground의 DTS 탭은 작성한 코드로부터 자동 생성되는 타입 선언 파일(.d.ts)을 보여준다. 내가 만든 함수나 타입이 외부에서 어떻게 보이는지, 어떤 타입 정보가 노출되는지 확인할 수 있다. 라이브러리를 만들 때 특히 유용하다.
타입 추론 활용해 verbosity(장황함) 줄이기
TypeScript 초보자들이 흔히 하는 실수는 불필요한 타입을 모든 곳에 명시하는 것이다.
// 나쁜 예 — 중복이고 장황하다
const numbers: number[] = [1, 2, 3];
const doubled: number[] = numbers.map((n: number): number => n * 2);
const sum: number = doubled.reduce((acc: number, cur: number): number => acc + cur, 0);
// 좋은 예 — 추론에 맡기고 필요한 곳만 명시한다
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
const sum = doubled.reduce((acc, cur) => acc + cur, 0);
두 코드 모두 TypeScript가 동일하게 타입을 검사한다. 아래 코드가 더 읽기 쉽고 유지보수하기 좋다.
반환 타입은 명시하는 것이 좋다
매개변수 타입은 필수지만 반환 타입은 추론에 맡길 수 있다. 그러나 공개 함수(API, 라이브러리)의 반환 타입은 명시하는 것이 좋다. 의도한 반환 타입을 선언하면 함수 내부에서 실수로 다른 타입을 반환했을 때 즉시 오류를 잡아낼 수 있다.
// 반환 타입 추론 — 내부 로직 변경 시 반환 타입이 조용히 바뀔 수 있다
function getUser(id: number) {
// 나중에 실수로 undefined를 반환해도 호출 측에서만 오류가 난다
return users.find(u => u.id === id);
}
// 반환 타입 명시 — 의도를 선언, 함수 내부에서 즉시 오류 감지
function getUser(id: number): User {
return users.find(u => u.id === id);
// 오류: Type 'User | undefined' is not assignable to type 'User'
// find()는 undefined를 반환할 수 있음을 알려준다
}
정리
| 개념 | 문법 | 예시 |
|---|---|---|
| 변수 타입 어노테이션 | let x: 타입 | let age: number = 25 |
| 함수 매개변수 타입 | (param: 타입) | (name: string) |
| 함수 반환 타입 | (): 타입 | function fn(): string |
| 반환 없음 | void | function log(): void |
| 선택적 매개변수 | param? | (name?: string) |
| 타입 추론 | 자동 | const x = 5 → number |
| 타입 에러 코드 | TSxxxx | TS2322, TS2345, TS2339 |
다음 장에서는 TypeScript 개발 환경을 더욱 강력하게 만들어 주는 VS Code 설정, ESLint, Prettier 연동 방법을 알아본다.