본문으로 건너뛰기
Advertisement

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.jsonrootDiroutDir을 설정했다면 다음 명령으로 프로젝트 전체를 컴파일한다.

npx tsc

src/hello.tsdist/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이름을 찾을 수 없음선언하지 않은 변수 사용
TS2531null일 수 있는 객체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
반환 없음voidfunction log(): void
선택적 매개변수param?(name?: string)
타입 추론자동const x = 5 → number
타입 에러 코드TSxxxxTS2322, TS2345, TS2339

다음 장에서는 TypeScript 개발 환경을 더욱 강력하게 만들어 주는 VS Code 설정, ESLint, Prettier 연동 방법을 알아본다.

Advertisement