TypeScript / / 2024. 10. 29. 05:06

TypeScript 인터페이스

1. 인터페이스란?

인터페이스는 객체의 타입을 정의할 때 사용되는 타입스크립트의 기능입니다. 인터페이스는 타입 별칭(type)과 비슷하게 특정 타입 구조를 정의하지만, 객체의 형태를 좀 더 명확하게 표현할 수 있다는 특징이 있습니다. 다음은 간단한 예시입니다:

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

위와 같이 정의한 Person 인터페이스는 객체의 타입을 정의하는 데 사용됩니다:

const person: Person = {
  name: "이정환",
  age: 27
};

인터페이스는 타입 별칭과 기능이 유사하지만, 주로 객체의 구조를 명확하게 기술할 때 많이 사용됩니다.

2. 선택적 프로퍼티(Optional Property)

인터페이스의 프로퍼티는 선택적으로 설정할 수도 있습니다. 선택적 프로퍼티는 ?를 사용하여 정의합니다:

interface Person {
  name: string;
  age?: number; // 선택적 프로퍼티
}

다음과 같이 age를 생략할 수 있습니다:

const person: Person = {
  name: "이정환"
};

3. 읽기 전용 프로퍼티(Readonly Property)

읽기 전용 프로퍼티는 객체가 생성된 이후에는 변경할 수 없도록 합니다. readonly 키워드를 사용하여 정의합니다:

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

이렇게 정의된 name 프로퍼티는 객체 생성 이후 변경이 불가능합니다:

const person: Person = {
  name: "이정환"
};

person.name = "홍길동"; // ❌ 오류 발생

4. 메서드 타입 정의하기

인터페이스 내에서 메서드의 타입을 정의할 수도 있습니다. 이는 함수 타입 표현식을 사용하거나 호출 시그니처를 사용하는 방식으로 정의합니다:

  • 함수 타입 표현식 사용:
  • interface Person { sayHi: () => void; }
  • 호출 시그니처 사용:
  • interface Person { sayHi(): void; }

5. 메서드 오버로딩(Method Overloading)

함수 타입 표현식을 사용할 경우 메서드 오버로딩은 불가능합니다. 하지만 호출 시그니처를 사용하면 메서드 오버로딩을 구현할 수 있습니다:

interface Person {
  sayHi(): void;
  sayHi(a: number): void;
  sayHi(a: number, b: number): void;
}

위처럼 여러 시그니처를 정의함으로써 다양한 형태로 메서드를 호출할 수 있습니다.

6. 하이브리드 타입 (Hybrid Type)

인터페이스를 이용해 함수와 객체의 형태를 동시에 가지는 하이브리드 타입을 정의할 수도 있습니다:

interface Func2 {
  (a: number): string; // 함수 타입 정의
  b: boolean;          // 프로퍼티 정의
}

const func: Func2 = (a) => "hello";
func.b = true;

이렇게 하면 함수인 동시에 객체 형태의 프로퍼티도 가지는 타입을 정의할 수 있습니다.

7. 타입 별칭과의 차이점

인터페이스와 타입 별칭은 대부분의 상황에서 비슷하게 사용될 수 있지만 몇 가지 차이점이 있습니다:

  1. Union과 Intersection 타입:
    • 타입 별칭에서는 Union(|)이나 Intersection(&) 타입을 정의할 수 있습니다. 예를 들어, 다음과 같이 사용할 수 있습니다:
    • type Type1 = number | string; // 가능 type Type2 = number & string; // 가능
    • 그러나 인터페이스에서는 이러한 Union이나 Intersection을 직접 정의할 수 없습니다:
    • interface Person { name: string; age: number; } | number; // ❌ 불가능
    • 따라서 인터페이스로 만든 타입을 Union 또는 Intersection으로 이용해야 한다면, 타입 별칭을 사용하거나 타입 주석에서 직접 사용하는 것이 필요합니다:
    • type Type1 = number | string | Person; type Type2 = number & string & Person; const person: Person & string = { name: "이정환", age: 27, };
  2. 확장 가능성:
    • 인터페이스는 확장이 용이합니다. extends 키워드를 통해 다른 인터페이스를 상속받아 재사용할 수 있습니다:
    • interface Person { name: string; age: number; } interface Employee extends Person { salary: number; }
    • 반면, 타입 별칭은 확장할 때 & 연산자를 사용해야 하며, 인터페이스의 확장성에 비해 제한적일 수 있습니다.
    • type Person = { name: string; age: number; }; type Employee = Person & { salary: number; };
  3. 선언 병합(Declaration Merging):
    • 인터페이스는 선언 병합이 가능합니다. 즉, 동일한 이름의 인터페이스를 여러 번 선언하면, 타입스크립트가 이를 병합하여 하나의 타입으로 만듭니다:
    • interface Person { name: string; } interface Person { age: number; } // 병합된 형태: const person: Person = { name: "이정환", age: 27 };
    • 타입 별칭은 이러한 병합이 불가능하며, 동일한 이름으로 선언하면 오류가 발생합니다.

8. 인터페이스 확장(Interface Extension)

인터페이스 확장이란 하나의 인터페이스를 다른 인터페이스들이 상속받아 중복된 프로퍼티를 정의하지 않도록 도와주는 기능입니다. 다음 예시를 보겠습니다:

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

interface Dog extends Animal {
  isBark: boolean;
}

interface Cat extends Animal {
  isScratch: boolean;
}

interface Chicken extends Animal {
  isFly: boolean;
}

여기서 Dog, Cat, Chicken 인터페이스는 모두 Animal 인터페이스를 확장하고 있습니다. 따라서 nameage 프로퍼티를 중복해서 정의하지 않아도 됩니다. 이처럼 인터페이스 확장은 중복 코드를 줄이고, 유지보수를 더 쉽게 해줍니다.

만약 Animal의 프로퍼티를 수정해야 할 경우, 확장된 모든 타입에서 자동으로 해당 변경 사항이 반영되기 때문에 더 효율적입니다:

interface Animal {
  name: string;
  color: string;
}

interface Dog extends Animal {
  breed: string;
}

const dog: Dog = {
  name: "돌돌이",
  color: "brown",
  breed: "진도",
};

이 예시에서 DogAnimal의 프로퍼티를 모두 상속받고, 추가적인 프로퍼티인 breed만 정의하면 됩니다.

9. 프로퍼티 재정의하기

인터페이스를 확장하면서 기존의 프로퍼티를 재정의하는 것도 가능합니다. 예를 들어:

interface Animal {
  name: string;
  color: string;
}

interface Dog extends Animal {
  name: "doldol"; // 타입 재정의
  breed: string;
}

여기서 Dog 인터페이스는 Animalname 프로퍼티를 "doldol"이라는 문자열 리터럴 타입으로 재정의했습니다. 하지만 이때 재정의하는 타입은 원래 타입의 서브 타입이어야 합니다. 만약 namenumber로 재정의하려고 하면 오류가 발생합니다:

interface Dog extends Animal {
  name: number; // ❌ 불가능
  breed: string;
}

이유는 DogAnimal의 서브타입이 되어야 하는데, name의 타입이 호환되지 않기 때문입니다.

10. 타입 별칭을 확장하기

인터페이스는 타입 별칭으로 정의된 객체 타입도 확장할 수 있습니다:

type Animal = {
  name: string;
  color: string;
};

interface Dog extends Animal {
  breed: string;
}

이렇게 하면 타입 별칭을 기반으로 새로운 인터페이스를 확장할 수 있습니다.

11. 다중 확장(Multiple Inheritance)

인터페이스는 다중 확장도 가능합니다. 여러 인터페이스를 동시에 확장하여 새로운 인터페이스를 만들 수 있습니다:

interface DogCat extends Dog, Cat {}

const dogCat: DogCat = {
  name: "",
  color: "",
  breed: "",
  isScratch: true,
};

DogCat 인터페이스는 DogCat을 동시에 확장하여 두 타입의 모든 프로퍼티를 가집니다.

12. 선언 병합(Declaration Merging)

타입 별칭은 동일한 스코프 내에 중복된 이름으로 선언할 수 없는 반면, 인터페이스는 선언 병합이 가능합니다. 즉, 동일한 이름의 인터페이스를 여러 번 선언하면, 타입스크립트가 이를 병합하여 하나의 타입으로 만듭니다:

type Person = {
  name: string;
};

type Person = { // ❌ 불가능
  age: number;
};

반면, 인터페이스의 경우 동일한 이름으로 여러 번 선언해도 병합이 됩니다:

interface Person {
  name: string;
}

interface Person {
  age: number;
}

// 병합된 형태:
const person: Person = {
  name: "이정환",
  age: 27
};

이렇게 동일한 이름의 인터페이스들이 합쳐지는 것을 **선언 병합(Declaration Merging)**이라고 부릅니다. 이 기능을 통해 코드의 확장성과 유지보수가 용이해집니다.

그러나, 동일한 이름의 프로퍼티를 서로 다른 타입으로 정의하면 오류가 발생합니다. 이를 충돌이라고 부르며, 선언 병합에서는 이러한 충돌이 허용되지 않습니다:

interface Person {
  name: string;
}

interface Person {
  name: number; // ❌ 충돌 발생
  age: number;
}

첫 번째 Person에서는 name 프로퍼티의 타입을 string으로, 두 번째에서는 number로 정의했기 때문에 타입 충돌이 발생합니다.

13. 정리

  • 인터페이스와 타입 별칭은 객체의 타입을 정의하는 데 사용되며, 기능적으로 유사하지만 특정 용도에서 차이점이 있습니다.
    • 인터페이스는 객체의 형태를 명확하게 정의하고 확장과 선언 병합이 용이합니다.
    • 타입 별칭UnionIntersection 타입을 정의하는 데 유리하며, 더 복잡한 타입 표현이 가능합니다.
  • 선택적 프로퍼티읽기 전용 프로퍼티를 통해 객체의 속성을 좀 더 유연하게 혹은 안전하게 정의할 수 있습니다.
  • 메서드 정의오버로딩이 가능하며, 하이브리드 타입도 정의할 수 있습니다.
  • 인터페이스 확장을 통해 중복된 코드를 줄이고, 유지보수를 더 쉽게 할 수 있습니다.
  • 선언 병합을 통해 동일한 이름의 인터페이스를 병합하여 코드를 확장할 수 있습니다.
  • 복잡한 타입을 정의할 때 인터페이스와 타입 별칭의 장단점을 잘 이해하고 적절히 사용하는 것이 중요합니다.

'TypeScript' 카테고리의 다른 글

JavaScript this정리  (0) 2024.10.29
자바스크립트, 타입스크립트 스코프,호이스팅  (2) 2024.10.29
TypeScript 정리3 (함수와 타입)  (0) 2024.10.25
TypeScript 정리2 (이해하기)  (3) 2024.10.25
TypeScript 정리1  (1) 2024.10.25
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유