Develop/TypeScript

[Typescript] implements / object index signatures

dawonny 2024. 3. 7. 01:01
728x90
반응형

interface와 implements 를 사용한 class 타입 확인

interface 는 object 의 타입을 지정할 때 사용할 수 있다.

그리고 또 다른 용도로, class 의 타입을 확인할 때에도 사용할 수 있다.

이 때, implements 키워드도 필요하다.

 

아래의 예시를 보자.

class Car {
  model: string;
  price: number = 1000;
  constructor(a: string) {
    this.model = a;
  }
}
let 붕붕이 = new Car('morning');

위의 Car 클래스에서 생성된 객체들은 model 과 price 속성을 가진다.

 

클래스가 특정 속성을 가지고 있는지 타입으로 확인하고 싶을 때,

interface 와 implements 키워드를 사용하여 아래와 같이 확인할 수 있다.

interface CarType {
  model: string;
  price: number;
}

class Car implements CarType {
  model: string;
  price: number = 1000;
  constructor(a: string) {
    this.model = a;
  }
}

클래스 이름 오른쪽에 implements 를 쓰고 interface 이름을 쓰면

해당 클래스가 인터페이스에 있는 속성을 모두 가지고 있는지 확인할 수 있다.

속성이 빠져있다면 에러로 알려주게된다.


implements 로는 확인만 할 수 있다

implements 는 interface 에 들어있는 속성을 가지고 있는지 확인하라는 의미이다.

클래스에 대한 필드와 타입을 할당하거나 변형하는 역할을 하지 않는다.

interface CarType {
  model: string;
  tax: (price: number) => number;
}

class Car implements CarType {
  model; // any 타입이 됨
  tax(a) { // a 파라미터는 any 타입이 됨
    return a * 0.1;
  }
}

위 예제를 보자.

CarType 을 implements 한 것은 model 이 string 타입이라고 자동으로 반영되는 것이 아니라

클래스 내부에서 model 은 any 타입으로 처리된다.

implements 는 클래스의 타입을 체크만 해주며, 타입을 할당하는 것은 아니란 것을 알 수 있다.


implements 와 extends 의 차이점

implements 는 클래스에 특정 필드와 함수를 가지고 있는지 확인하고 싶을 때 사용한다.

클래스 간에 복잡하게 상속을 하는 경우, 어떤 필드와 함수가 포함되어 있는지 추론하기 어려울 때

implements 를 사용하여 타입 체크를 할 수 있다. (클래스를 많이 만들 때 일종의 가이드북 역할을 해준다)

 

class A extends B 는 B 에 있던 필드와 함수를 A 로 복사해준다.

extends 는 클래스 간에 상속을 구현할 때 사용된다.

 

implements 는 단지 타입 체크만 해주는 반면, extends 는 상속을 통해 실제 속성과 메서드를 복사한다.


Index Signature 란

Index signature 는 object 의 속성 이름과 값의 타입을 미리 지정할 수 있는 기능이다.

속성 이름을 미리 알 수 없거나, 속성이 많을 경우에 유용하다.

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

index signature 를 사용하려면 위와 같이 interface 에 [key: 타입] 형식을 추가한다.

key는 속성이름을 나타내고, 그 값의 타입을 지정할 수 있다.

 

따라서 다음과 같은 객체를 만들 수 있다.

let obj: StringOnly = {
  name: 'kim',
  age: '20',
  location: 'seoul'
};

 

모든 속성의 값을 String 으로 제한했기 때문에, 만약 값의 타입이 다양해질 수 있는 상황에서는 제약이 될 수 있다.


Index Signature 과 다른 속성 함께 사용하기

Index signature 는 다른 속성과 함께 사용될 수 있다.

하지만 모든 속성이 그 index signature 와 일치해야한다.

예를 들어 아래의 예시를 보자

interface NotAllowed {
  [key: string]: string;
  age: number; // 에러 발생
}

 

모든 속성이 String 값을 가져야한다는 규칙을 세워놓았지만,

age 라는 속성은 number 값을 가지기 때문에 에러가 발생한다.

 

이 문제를 해결하려면 아래와 같이 작성할 수 있다.

interface Allowed {
  [key: string]: string | number;
  age: number; // 가능
}

배열 형태로 index signature 사용하기

interface StringArray {
  [key: number]: string;
}

let obj: StringArray = {
  0: 'kim',
  1: '20',
  2: 'seoul'
};

 

index signature 를 이용하면 객체를 배열처럼 사용할 수 있다.

 

위 StringArray 라는 interface는 number 타입의 key에 string 값을 할당하는 객체를 배열처럼 다룰 수 있도록 한다.

 


재귀적 index signature 사용하기

interface MyType {
  'font-size' : {
    'font-size' : {
      'font-size' : number
    }
  }
}

let obj = {
  'font-size' : {
    'font-size' : {
      'font-size' : 14
    }
  }
}

 

위 예제와 같이 object안에 object 안에 object 가 들어있는 경우를 생각해보자.

실제로 이런 경우가 많지는 않겠지만 이렇게 중첩된 object 들을 한번에 타입지정하고 싶으면 어떻게 해야할까?

 

해결 방법은 index signature를 아래와 같이 재귀적으로 사용하는 것이다.

interface MyType {
  'font-size': MyType | number;
}

 

위 방식은 중첩된 구조를 간결하게 타입지정할 수 있다.

재귀적인 방식이라 object가 얼마나 깊어지든지 유연하게 대응할 수 있을 것이다.

 

 

 


ref: 코딩애플, Typescript 공식문서

728x90
반응형