Learning
레슨 4 / 8·20분

함수와 타입 가드

함수 타입 선언

TypeScript에서 함수의 매개변수와 반환 타입을 명시하면 호출 시 잘못된 인자를 컴파일 단계에서 잡을 수 있습니다. 선택적 매개변수, 기본값, 나머지 매개변수에도 타입을 지정할 수 있습니다.

typescript
// 기본 함수 타입
function add(a: number, b: number): number {
  return a + b;
}

// 화살표 함수 타입
const multiply = (a: number, b: number): number => a * b;

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

// 나머지 매개변수
function sum(...nums: number[]): number {
  return nums.reduce((a, b) => a + b, 0);
}

// 함수 타입 별칭
type Formatter = (value: number) => string;
const toWon: Formatter = (v) => v.toLocaleString() + "원";

// void와 never
function log(msg: string): void { console.log(msg); }
function throwError(msg: string): never { throw new Error(msg); }

함수 오버로드

typescript
// 오버로드 시그니처
function format(value: string): string;
function format(value: number): string;
function format(value: Date): string;
function format(value: string | number | Date): string {
  if (typeof value === "string") return value.trim();
  if (typeof value === "number") return value.toFixed(2);
  return value.toISOString();
}

format("hello");          // OK → string
format(3.14);             // OK → "3.14"
format(new Date());       // OK → ISO 문자열

타입 가드 (Type Guards)

타입 가드는 조건문 내에서 타입을 좁혀 (narrowing) 특정 타입의 속성에 안전하게 접근하게 합니다. typeof, instanceof, in 연산자, 그리고 사용자 정의 타입 가드를 사용합니다.

typescript
// typeof 가드
function process(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase(); // string으로 좁혀짐
  }
  return value.toFixed(2); // number로 좁혀짐
}

// in 연산자 가드
type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    animal.swim(); // Fish로 좁혀짐
  } else {
    animal.fly();  // Bird로 좁혀짐
  }
}

// 사용자 정의 타입 가드 (is 키워드)
function isString(value: unknown): value is string {
  return typeof value === "string";
}

function handle(input: unknown) {
  if (isString(input)) {
    console.log(input.toUpperCase()); // string 확정
  }
}

Discriminated Union

typescript
// 판별 유니온 — type 필드로 구분
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rectangle":
      return shape.width * shape.height;
    case "triangle":
      return (shape.base * shape.height) / 2;
  }
}

// exhaustiveness check — 모든 케이스를 처리했는지 확인
function assertNever(x: never): never {
  throw new Error("Unexpected: " + x);
}
💡

Discriminated Union + switch문은 TypeScript에서 가장 강력한 패턴 중 하나입니다. 새로운 타입을 추가하면 switch에서 처리하지 않은 케이스를 컴파일러가 알려줍니다.