TypeScript 문법과 사용법

Cluade 4 Sonnet을 이용해 작성한 글입니다.

TypeScript는 JavaScript에 정적 타입 시스템을 추가한 프로그래밍 언어이다. Microsoft가 개발했으며, JavaScript의 모든 기능을 포함하면서도 추가적인 타입 안전성을 제공한다.

기본 타입 시스템

TypeScript의 핵심은 타입 시스템이다. JavaScript의 동적 타입과 달리, 컴파일 시점에 타입 오류를 발견할 수 있다.

// 기본 타입 선언
let name: string = "홍길동";
let age: number = 25;
let isActive: boolean = true;

// 배열 타입
let numbers: number[] = [1, 2, 3, 4, 5];
let fruits: Array<string> = ["사과", "바나나", "오렌지"];

// 객체 타입
let user: { name: string; age: number } = {
  name: "김철수",
  age: 30
};

인터페이스와 타입 별칭

복잡한 객체 구조를 정의할 때는 인터페이스나 타입 별칭을 사용한다.

// 인터페이스 정의
interface User {
  id: number;
  name: string;
  email: string;
  isAdmin?: boolean; // 선택적 속성
}

// 타입 별칭
type Status = "pending" | "approved" | "rejected"; // 유니온 타입

// 사용 예시
const createUser = (userData: {name: string, email: string, isAdmin?: boolean}): User => {
  return {
    id: Date.now(),
    ...userData
  };
};

함수 타입

함수의 매개변수와 반환값에 타입을 지정할 수 있다.

// 함수 선언
function calculateSum(a: number, b: number): number {
  return a + b;
}

// 화살표 함수
const multiply = (x: number, y: number): number => x * y;

// 선택적 매개변수와 기본값
function greet(name: string, greeting: string = "안녕하세요"): string {
  return `${greeting}, ${name}님!`;
}

// 제네릭 함수
function getFirstElement<T>(array: T[]): T | undefined {
  return array[0];
}

클래스와 상속

TypeScript는 ES6 클래스를 확장하여 접근 제한자와 추상 클래스를 지원한다.

// 기본 클래스
class Animal {
  protected name: string; // 상속받은 클래스에서만 접근 가능
  
  constructor(name: string) {
    this.name = name;
  }
  
  public makeSound(): void {
    console.log("동물이 소리를 냅니다");
  }
}

// 상속
class Dog extends Animal {
  private breed: string; // 현재 클래스에서만 접근 가능
  
  constructor(name: string, breed: string) {
    super(name); // 부모 생성자 호출
    this.breed = breed;
  }
  
  public makeSound(): void {
    console.log(`${this.name}가 멍멍 짖습니다`);
  }
}

제네릭

타입을 매개변수처럼 사용하여 재사용 가능한 컴포넌트를 만들 수 있다.

// 제네릭 인터페이스
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// 사용 예시
const userResponse: ApiResponse<User> = {
  data: { id: 1, name: "이영희", email: "lee@example.com" },
  status: 200,
  message: "성공"
};

// 제네릭 클래스
class DataStorage<T> {
  private items: T[] = [];
  
  addItem(item: T): void {
    this.items.push(item);
  }
  
  getItems(): T[] {
    return [...this.items];
  }
}

유니온과 교차 타입

여러 타입을 조합하여 더 복잡한 타입을 만들 수 있다.

// 유니온 타입 (OR 관계)
type StringOrNumber = string | number;
let value: StringOrNumber = "hello"; // 또는 123

// 교차 타입 (AND 관계)
interface Name {
  name: string;
}

interface Age {
  age: number;
}

type Person = Name & Age; // name과 age 속성을 모두 가져야 함

const person: Person = {
  name: "박민수",
  age: 28
};

실용적인 사용법

타입 가드

런타임에서 타입을 확인하는 방법이다.

// typeof 타입 가드
function processValue(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase(); // string 메서드 사용 가능
  }
  return value.toString(); // number 메서드 사용 가능
}

// 사용자 정의 타입 가드
function isUser(obj: any): obj is User {
  return obj && typeof obj.name === "string" && typeof obj.email === "string";
}

null 안전성

TypeScript는 null과 undefined를 엄격하게 검사한다.

// strictNullChecks 옵션 활성화 시
let userName: string | null = getUserName();

// null 체크 필요
if (userName !== null) {
  console.log(userName.length); // 안전하게 사용 가능
}

// 옵셔널 체이닝 사용
console.log(userName?.length);

설정과 컴파일

TypeScript 프로젝트를 시작하려면 tsconfig.json 파일이 필요하다.

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

TypeScript는 JavaScript 개발자에게 점진적인 마이그레이션 경로를 제공한다. 기존 JavaScript 코드에 타입을 하나씩 추가해가며 안전성을 높일 수 있으며, 개발 도구의 자동완성과 리팩터링 기능도 크게 향상된다.