[TypeScript] 인터페이스

윤지·2024년 11월 20일

TypeScript

목록 보기
6/12
post-thumbnail

인터페이스는 TypeScript에서 객체 타입을 정의하는 데 최적화된 문법임. 타입 별칭이 변수를 할당하는 듯한 느낌을 주는 반면, 인터페이스는 클래스 문법과 유사하게 중괄호를 열어 바로 선언함


1. 인터페이스의 기본 사용

인터페이스는 객체의 타입을 지정할 때 사용됨

interface Person {
  name: string;
  age: number;
}

const user: Person = {
  name: "Alice",
  age: 30,
};

2. 객체 속성 기법

선택적 속성 (Optional)

interface User {
  name: string;
  age: number;
  email?: string; // 선택적 속성
}

const user1: User = { name: "Alice", age: 30 }; // ✅ OK
const user2: User = { name: "Bob", age: 25, email: "bob@example.com" }; // ✅ OK

읽기 전용 속성 (Readonly)

interface ReadonlyUser {
  readonly id: number;
  name: string;
  age: number;
}

const user: ReadonlyUser = { id: 1, name: "Alice", age: 30 };
// user.id = 2; // ❌ 오류: 'id'는 읽기 전용임

3. 인덱스 시그니처

객체의 키와 값을 동적으로 정의할 수 있음

interface StringDict {
  [key: string]: string;
}

const dict: StringDict = {
  name: "Alice",
  city: "Seoul",
};

4. 함수 타입

인터페이스를 사용해 함수 타입도 정의 가능

interface MathFunc {
  (x: number, y: number): number;
}

const add: MathFunc = (a, b) => a + b;
const multiply: MathFunc = (a, b) => a * b;

console.log(add(5, 3));      // 출력: 8
console.log(multiply(4, 2)); // 출력: 8

하지만 이 방식은 직관적이지 않아 실무에서 인터페이스로 함수 타입을 지정하는 경우가 거의 없음. 대신 타입 별칭이 더 편리하고 가독성이 높아 선호됨

타입 별칭을 사용한 함수 타입 정의

type MathFunction = (x: number, y: number) => number;

5. 선언병합(Declaration Merging)

동일한 이름의 인터페이스를 여러 번 선언할 경우 이들이 자동으로 병합되어 하나의 인터페이스로 통합됨

인터페이스는 코드 작성 위치에 관계없이 병합됨. 이는 타입스크립트의 정적 타입 시스템의 특징 중 하나임

interface User {
    name: string;
    age: number;
  }

  interface User {
    address: string;
  }

  let user: User = {
    name: "Alice",
    age: 30,
    address: "Seoul",
  };

타입별칭 비교

중복 식별자

type UserType = {
  name: string;
  age: number;
};

// ❌ Error
type UserType = {
  address: string;
};

선언 병합은 인터페이스의 주요 단점 중 하나로, 협업 시 예상치 못한 오류를 일으킬 수 있음.

실무에선 일반적으로 객체 타입은 interface로, 기타 커스텀 타입은 type 별칭으로 지정함

✅ 하지만 인터페이스로도 함수 타입 지정이 가능함(강사님 선호)

자동완성 툴팁

타입별칭: 자동완성 툴팁에서 더 상세한 정보 제공. 인터페이스: 기본적인 타입 정보만 표시하는 경향. 복잡한 객체 구조에서 덜 상세할 수 있음

type User = {
  name: string;
  age: number;
  email: string;
};

//마우스를 올려보면 속성이 툴팁으로 보임
const user: User = {
  // 여기서 속성을 입력할 때 자동완성 툴팁이 제공됨
  name: "Alice",
  age: 30,
  email: "alice@example.com"
};

// user. 을 입력하면 name, age, email 속성에 대한 자세한 정보가 툴팁으로 표시됨
console.log(user.name);

5. 인터페이스의 상속

인터페이스: 다른 인터페이스를 상속받아 확장 가능

interface Shape {
  color: string;
}

interface Circle extends Shape {
  radius: number;
}

const myCircle: Circle = {
  color: "red",
  radius: 10,
};

타입 별칭을 사용한 인터페이스 확장

interface Shape {
  color: string;
}

interface Circle {
  radius: number;
}

type CircleShape = Shape & Circle;

const myCircle: CircleShape = {
  color: "red",
  radius: 5,
};

console.log(myCircle.color);  // 출력: red
console.log(myCircle.radius); // 출력: 5

다중 상속 패턴

interface Person {
    name: string;
    age: number;
  }

  const person: Person = {
    name: "John",
    age: 20,
  };

  interface Address {
    city: string;
    readonly zipCode?: string;
  }

  interface Employee extends Person, Address {
    employeeId: string;
  }

  const employee: Employee = {
    name: "kisu",
    age: 20,
    employeeId: "1234",
    city: "seuol",
    zipCode: "131111",
  };

상속 예제

interface Animal {
  name?: string;
  sound(): void; // sound: () => void
}

interface Pet extends Animal {
  play?(): void; // play: () => void
}

// 단축 메서드명
const dog: Pet = {
  name: "뽀삐",
  sound() {
    console.log("멍멍");
  },
  play() {
    console.log("놀기");
  },
};

if (dog.play) dog.play();

마지막 줄의 if 문은 타입가드

if (dog.play) dog.play();

이렇게 함으로써, play 메서드가 존재할 때만 호출되어 더 안전한 코드 실행이 가능해짐

6. 인터페이스 제네릭

interface Box<T> {
  contents: T;
}

const numberBox: Box<number> = { contents: 42 };
const stringBox: Box<string> = { contents: "Hello, TypeScript!" };

고급 사용 패턴: 제네릭 + 상속

 //1. Container 인터페이스 정의: 제네릭 T를 사용해 value 속성을 정의
 interface Container<T> {
    value: T;
  }

//2. Box 인터페이스 정의: Container<U>를 상속받고, 추가적으로 scale과 inStock 속성을 정의
  interface Box<T, U> extends Container<U> {
    label: string;
    scale?: T;
    inStock?: U;
  }

//3. Container 타입 객체 생성: number 타입의 value를 가지는 container 객체
  const container: Container<number> = {
    value: 10,
  };

//4. Box 타입 객체 생성: number 타입의 scale과 boolean 타입의 inStock을 가지는 box 객체
  const box: Box<number, boolean> = {
    label: "grid box",
    value: true, // value는 boolean (Container<U>에서 상속됨)
    scale: 10, // scale은 number (Box<T>에서 정의됨)
  };

인터페이스와 타입별칭의 차이점

구분인터페이스타입별칭
객체 타입 정의주로 사용가능
함수, 유니온, 인터섹션제한적 사용 가능 (함수 타입 지정 가능하나 실무에서 잘 사용하지 않음)가능 (더 편리하고 가독성이 높아 선호됨)
선언 병합지원 (동일한 이름의 인터페이스를 여러 번 선언 시 자동으로 병합)지원하지 않음 (중복 선언 시 오류 발생)
상속extends 키워드로 다중 상속 가능& 연산자로 타입 확장 가능
자동완성 툴팁기본적인 타입 정보만 표시더 상세한 정보 제공
제네릭지원지원

출처: 수코딩

profile
프론트엔드 공부 기록 아카이빙🍁

0개의 댓글