[Interface(인터페이스)]

Interface(인터페이스)

- 타입에 이름을 지어주는 또 다른 작명 문법이자 상호간에 약속된 문법이며 객체의 구조를 정의하는데 특화된 문법이다. Type Alias에는 없는 상속, 합침 등의 Type Alias에는 없는 특수 기능을 제공한다. 작성 방식은 Type Alias와 비슷한 형태로 작성한다. 하지만 정의하며 "="를 사용하지 않는다.

interface Person {
  name: string,
  age?: number,
  sayHi(): void,
  sayHi(a:number, b:number): void
}
const person:Person = {
  name: "Tom",
  age: 10,
  sayHi:() => {
    console.log("Hi")
  }
}
person.sayHi();
person.sayHi(1,2);

위의 코드와 같이 작성한다. InterfaceType Alias와 같이 타입을 정의하고, 객체의 method 타입도 정의 가능하며 Optional Property까지 적용이 가능하다. 

 

하지만 주의해야할 점이 몇가지 있다.

1. 객체 변수를 선언할 때 내부에 method가 존재하는 경우 Interface에 method의 타입을 정의할 때 Call Signature(호출 시그니쳐)를 통해 method의 타입을 정의하게 되면 선언한 객체가 함수 취급을 받기 때문에 오류가 발생한다. 따라서 method의 타입을 정의할 때에는 Call Signature 앞에 method의 이름을 적어주어야 한다.
interface Person {
  name: string,
  age?: number,
  (): void,		//X -> person is object
  sayHi(): void		// O
}
const person:Person = {
  name: "Tom",
  age: 10,
  sayHi:() => {
    console.log("Hi")
  }
}
2. InterfaceUnion Type이나 Intersection Type으로 정의가 되지 않는다. 독립적인 타입 정의 방식이기 때문에 다른 타입과 혼용이 불가능하다. 따라서 Interface를 정의해둔 후 해당 Interface와 다른 타입을 타입 주석으로 처리해주거나 별도의 Type Alias에 Union or Intersection 요소로 할당을 해서 사용해주어야 한다.
interface Person {
  name: string,
} | number //Error, interface can be a Union Type or Intersection Type

const person:Person | number = { //Not Error
  name: "Tom",
}
3. Interface의 이름을 정의할 때에는 대문자 "I"를 붙여서 이름을 짓는 경우가 있다고 한다. 하지만 JS를 주로 사용하던 개발자 입장에서는 Snake Case, Camel Case, Pascal Case 같이 다양한 방식으로 변수명을 입력했었는데 추가적인 작명 방식이 생겨나면 좋지 않기 때문에 어느 하나에 국한되지 않고 자신이 속한 회사나 그룹의 방식을 따라가는 것이 좋다.

 

[Extends Interface(인터페이스 확장)]

Extends Interface(인터페이스 확장)

- extends 키워드를 통해 다른 Interface로부터 해당 Interface가 갖고 있는 모든 property를 확장하여 사용할 수 있도록하는 기법이다. 사용 예시는 아래와 같다.

Interface Animal {
  name: string,
  age: number,
}
Interface Dog extends Animal {
  isBark: boolean
}
Interface Cat extends Animal {
  isScratch: boolean
}
const dog:Dog = {
  name: "Bob",
  age: 10,
  isBark: true
}
const cat:Cat = {
  name: "Bob",
  age: 10,
  isScratch: false
}

Interface 확장을 통해 property를 받아서 사용하는 경우를 Interface 상속이라고도 하는데, Type Alias에는 없지만 Interface에는 존재한다는 특별한 기능이다. 또한 Interface 상속을 받았다고해서 상속 받은 Interface의 속성의 타입을 변경할 수 없는 것은 아니다. 속성의 타입을 변경할 수 있지만 조건이 있다. 변경하려는 속성의 타입이 상속 받은 Interface의 속성의 타입의 Serve Type이어야 한다는 것이다. 글로 보면 이해하기 헷갈리니 코드를 통해 확인해보자.

interface A {
  name: string,
  age: number
}
interface B extends A { //Error
  name: number,
  age: number,
  isTrue: boolean
}
interface C extends A { //Not Error
  name: "Tom",
  age: number,
  isTrue: boolean
}

위의 코드 예시를 보면 interface B는 A로부터 상속을 받아 속성들을 사용하는데 name property의 타입을 number로 재정의 하였다. 이런 경우 두 타입의 계층이 같기 때문에 상속에 오류가 발생한다.

반면 interface C는 동일하게 A로부터 상속을 받고 name property의 타입을 String Literal Type으로 재정의 하였다. String Literal TypeString TypeServe Type이므로 오류가 발생하지 않는 것이다.

상속을 받았다는 것 자체가 한 쪽이 다른 한 쪽의 Serve Type으로 취급 되는 것이기에 내부 속성들도 규칙에 맞게 움직여야 하는 것이다.

Interface는 Type Alias로 정의된 타입도 상속이 가능하다. 객체 형태로 정의된 타입은 모두 확장이 가능, 상속 받는 것이 가능하다.

 

다중 확장

- 말그대로 확장을 다중으로 하는 것이다. 특정 Interface가 서로 다른 Interface들 모두를 상속받는 것을 의미한다. 어려운 내용은 아니기에 예시 코드만 작성하고 마무리하겠다.

interface A {
  name: string,
  age: number
}
interface B {
  isTrue: boolean,
  color: string
}
interface C extends A,B {
  isReal: boolean
}

const something:C = {
  name: "something",
  age: 10,
  isTrue: true,
  color: "red",
  isReal: "false"
}

 

[Declaration Merging(선언 합침)]

Declaration Merging(선언 합침)

- Type Alias와 달리 Interface는 중복 선언이 가능하며 중복 선언 시 모든 property들은 하나로 합쳐지게 된다. 동일한 Interface를 반복하여 정의하고 내부 속성이 다르다면 자체적으로 하나의 Interface로 합쳐지게 된다. 하지만 주의해야할 점이 있는데, 반복하여 Interface를 정의할 때 동일한 속성이 동일한 타입을 갖고 반복 정의되면 문제가 되지 않지만 동일한 속성이 다른 타입으로 정의되면 타입 충돌이 발생한다. 아래 코드를 예시로 보자.

interface A {
	name: string
}
interface A {
	age: number
}
interface A {
	name: number // Error, typeof name is "string"
}
interface A {
	name: "hello" // Error, typeof name is "string" not only another type but serve type too
}

const a: A = {
	name: "a",
	age: 1	
}

Interface A의 name property는 String Type으로 정의되어 있기 때문에 String Type이 아닌 다른 타입으로 정의가 되면 타입 충돌이 발생한다. Extends Interface(인터페이스 확장)에서는 Serve Type이라면 중복 선언이 가능했지만 선언 합침에 있어서는 불가능하다.

 

Declaration Merging(선언 합침)은 기본적인 코드보다 모듈 보강에 잘 사용된다고 한다. 외부 라이브러리의 모듈을 불러와 사용할 때 Interface가 이미 정해져있기 때문에 사용 과정에서 속성을 추가할 수가 없다. 이런 경우 추가하고자 하는 속성을 Interface에 넣기 위해 다시 선언을 하여 Interface가 합쳐지도록 하는 것이다. 자세한 과정을 모듈을 불러온 후 해야할 작업이 더 있기에 그 부분에 대해서는 나중에 다루어 보도록 하자.

interface Lib {
  name:string,
  age: number
}
interface Lib {
  color: string
}
const lib:Lib = {
  name: "a",
  age: 10,
  color: "black"
}

 

참고 : Inflearn - 한 입 크기로 잘라먹는 Typescript(이정환)


[Interface]

TS 내에서 Class나 object의 형태를 지정해주는 방법은 두가지가 있다. Type 키워드를 사용해서 형태와 타입을 지정해주는 방식과 Interface를 사용하는 방식 두가지가 있다. 두가지 방법은 Abstract Class를 대체하는 방식이기도 한데, Abstract Class는 TS에서 JS로 컴파일 되는 과정에서 사라지지 않고 일반 Class로 남아있지만 Interface와 Type은 컴파일 과정에서 사라지기 때문에 코드를 경량화 시킬 수 있고 문법적으로 더 편하게 사용할 수 있다. 하지만 두 가지 방법 중에서 Interface를 사용하는 것이 object와 class에 한해서 더 편리하고 유용하다.

1. 첫번째로 확장과 상속에 있어서 Interface가 편리하고 쉽다.

type은 확장을 하기 위해서는 새로운 타입을 정의하면서 기존의 타입을 부여하면서 AND 연산자를 통해서 확장할 속성을 별도로 입력해주어야 한다. 하지만 Interface는 Extends 키워드 하나만으로 간단하게 타입 확장이 가능하다.

//Type
type A = {
  name: string
}
type B = A & {
  age: number
}
const a = getA();
a.name;
a.age;
//Interface
interface A {
  name: string
}
interface B extends A {
  age: number
}
const b = getB();
b.name;
b,age;

 

2. 두번째로 새로운 field를 추가할 때 Interface의 Declaration Merging(선언 합침)이라는 방식으로 간단하게 새로운 filed를 추가할 수 있다. type은 한 번 정의한 type을 다시 정의할 수 없다. 하지만 Interface는 제한 없이 중복적으로 정의하여도 TS 자체적으로 하나로 합쳐버리는 선언 합침이 존재하기 때문에 중복 정의가 가능하다.

//Type
type A = {
  name: string
}
type A = {		//Error: Duplicate identifier "A"
  age: number
}
//Interface
interface B {
  name: string
}
interface B {
  age: number
}
const b: B = {
  name: "B",
  age: 10
}

 

 

참고 : Nomad Coders - 타입스크립트로 블록체인 만들기

'Typescript' 카테고리의 다른 글

[TypeScript] - (7) Generic(제네릭)  (2) 2024.08.07
[TypeScript] - (6) Class  (0) 2024.08.05
[Typescript] - (4) Typescript 함수  (0) 2024.07.31
[Typescript] - (3) Typescript 이해하기  (0) 2024.07.26
[TypeScript] - (2) Typescript 기본  (3) 2024.07.23

+ Recent posts