본문 바로가기

온라인 강의(유데미, 인프런 등)/한 입 크기로 잘라먹는 타입스크립트(인프런)

[한 입 크기로 잘라먹는 타입스크립트] 인터페이스

반응형

1. 인터페이스

인터페이스란 타입 별칭과 동일하게 타입에 이름을 지어주는 또 다른 문법이다. 인터페이스는 타입 별칭과 문법만 조금 다를 뿐 기본적인 기능은 거의 같다고 볼 수 있다.

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

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

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

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

// 읽기 전용 프로퍼티
interface Person {
  readonly name: string;
  age?: number;
}

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

person.name = '홍길동' // ❌

 # 메서드 타입 정의하기

interface Person {
  readonly name: string;
  age?: number;
  sayHi: () => void;
}

 

함수 타입 표현식을 이용해 sayHi 메서드의 타입을 정의했다. 함수 타입 표현식 말고 다음과 같이 호출 시그니쳐를 이용해 메서드의 타입을 정의할 수도 있다.

interface Person {
  readonly name: string;
  age?: number;
  sayHi(): void;
}

# 메서드 오버로딩

함수 타입 표현식으로 메서드의 타입을 정의하면 메서드의 오버로딩 구현이 불가능하다. 그러나 호출 시그니처를 이용해 메서드의 타입을 정의하면 오버로딩 구현이 가능하다.

interface Person {
  readonly name: string;
  age?: number;
  sayHi: () => void; 
  sayHi: (a: number, b: number) => void; // ❌
}

interface Person {
  readonly name: string;
  age?: number;
  sayHi(): void;
  sayHi(a: number): void;
  sayHi(a: number, b: number): void;
}

# 하이브리드 타입

인터페이스 또한 함수이자 일반 객체인 하이브리드 타입을 정의할 수 있다.

interface Func2 {
  (a: number): string;
  b: boolean;
}

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

# 주의할 점 

대부분의 상황에 타입 별칭과 동일하게 동작하지만 몇 가지 차이점이 있다. 타입 별칭에서는 다음과 같이 Union이나 Intersection 타입을 정의할 수 있었지만 인터페이스에서는 불가능하다.

type Type1 = number | string;
type Type2 = number & string;

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. 인터페이스 확장하기

인터페이스 확장이란 하나의 인터페이스를 다른 인터페이스들이 상속받아 중복된 프로퍼티를 정의하지 않도록 도와주는 문법이다.

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

interface Dog extends Animal {
  breed: string;
}

interface Cat extends Animal {
  isScratch: boolean;
}

interface Chicken extends Animal {
  isFly: boolean;
}

interface 타입이름 extends 확장 할 타입이름 형태로 extends 뒤에 확장할 타입의 이름을 정의하면 해당 타입에 정의된 모든 프로퍼티를 다 가지고 오게 된다. 따라서 Dog, Cat, Chicken 타입은 모두 Animal 타입을 확장하는 타입이기 때문에 name, age 프로퍼티를 갖게 된다.

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

interface Dog extends Animal {
  breed: string;
}

(...)

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

이때 확장 대상 타입인 Animal은 Dog 타입의 슈퍼타입이 된다.

# 프로퍼티 재 정의하기

확장과 동시에 프로퍼티의 타입을 재 정의 하는 것 또한 가능하다.

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

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

Dog 타입은 Animal 타입을 확장하며 동시에 name 프로퍼티의 타입을 String 타입에서 “doldol” String Literal 타입으로 재정의 했다. 이렇게 확장받는 타입에서 프로퍼티의 타입을 재정의 할 수 있다.

주의할 점은 프로퍼티를 재 정의할 때 원본 타입을 A 재 정의된 타입을 B라고 하면 반드시 A가 B의 슈퍼 타입이 되도록 재정의 해야 한다. 따라서 다음과 같이 name을 Number 타입으로 재 정의 하는 것은 불가능하다. Dog 타입이 Animal 타입을 확장한다는 것은 Animal 타입의 서브타입이 된다는 의미인데, 저렇게 되면 더 이상 A가 B의 슈퍼 타입이 되지 않기 때문이다(string은 number의 슈퍼 타입이 아니므로).

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

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

# 타입 별칭을 확장하기

인터페이스는 인터페이스 뿐만 아니라 타입 별칭으로 정의된 객체도 확장할 수 있다.

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

interface Dog extends Animal {
  breed: string;
}

# 다중 확장

또 여러 개의 인터페이스를 확장하는 것도 가능하다.

interface DogCat extends Dog, Cat {}

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

 

3. 인터페이스 합치기

타입 별칭은 동일한 스코프 내에 중복된 이름으로 선언할 수 없는 반면 인터페이스는 가능하다.

type Person = {
  name: string;
};

type Person = { ❌
  age: number;
};

interface Person {
  name: string;
}

interface Person { // ✅
  age: number;
}

 

이렇게 되는 이유는 중복된 이름의 인터페이스 선언은 결국 모두 하나로 합쳐지기 때문이다. 따라서 위 코드에 선언한 Person 인터페이스들을 결국 합쳐져 다음과 같은 인터페이스가 된다.

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

# 주의할 점

그런데 만약 다음과 같이 동일한 이름의 인터페이스들이 동일한 이름의 프로퍼티를 서로 다른 타입으로 정의한다면 오류가 발생한다.

interface Person {
  name: string;
}

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

첫번째 Person에서는 name 프로퍼티의 타입을 string 으로 두번째 Person 에서는 name 프로퍼티의 타입을 number 타입으로 정의했다. 이렇게 동일한 프로퍼티의 타입을 다르게 정의한 상황을 ‘충돌’ 이라고 표현하며 선언 합침에서 이런 충돌은 허용되지 않는다.

 

https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard

 

반응형