본문 바로가기
front/ts

[타입스크립트] 프로퍼티를 조작할 때 주석을 유지하기

by juniKang 2022. 7. 20.

 

interface A {
  apple: string
  ...
}

interface B extends A { // -->error
  apple: string | number
  ...
}
/** -error message-
Interface 'NumberStringApple' incorrectly extends interface 'StringApple'.
  Types of property 'apple' are incompatible.
    Type 'string | number' is not assignable to type 'string'.
      Type 'number' is not assignable to type 'string'.ts(2430)

*/

A 인터페이스를 B에서 상속받는데, 특정 프로퍼티에 number를 추가하고 싶을 때, 그냥 extends를 쓰면 에러가 난다. 어떻게 이룰 수 있을까? 

 

스택오버플로우의 한 답변에서는 타입스크립트 유틸타입의 Omit을 사용하는 걸 추천한다.

https://stackoverflow.com/a/51507473/18405167

Omit으로 'x' 키값을 내보내고, x를 재정의 하고있다.

그러나 만약, 내가 수정하고자 하는 프로퍼티에 주석이 달려있다면 주석도 사라지기 때문에, 재정의할 때 주석을 다시 달아줘야한다.

interface A {
  /** 엄청난 길이의 주석 */
  apple: string
  ...
}

interface B extends Omit<A, 'apple'> {
  /** 엄청난 길이의 주석을 한번 더 써준다. */
  apple: string | number
}

재정의 해야할 프로퍼티가 많다면, 같은 작업을 여러번 해야하고 코드도 장황해진다.

 


 주석을 남기면서 원하는 프로퍼티에 number만 추가하는 방법을 고민한 결과 아래 순서로 단계를 나누었다.

1. 원하는 프로퍼티만 뽑아서 number를 추가시킨 타입을 정의한다.

2. 1번에서 뽑은 프로퍼티를 원본에서 제거한 타입을 정의한다.

3. 둘을 합친다.

 

이렇게 하면, 1번에서는 원하는 프로퍼티의 주석까지 가져온 다음 number를 추가했기 때문에, 합쳤을 때 주석이 그대로 남아있게 된다. 대신 위 예제처럼 인터페이스로는 구현하는 방법은 아직 모르겠고, 타입으로 구현한 다음 인터페이스에서 extends로 상속 받아 사용할 수 있다.

 

1번부터 보자 : 원하는 프로퍼티만 뽑아서 number를 추가시킨 타입을 정의한다.

interface A {
  /** 엄청난 길이의 주석 */
  apple: string
  ...
}

type AddTypes<T, extends keyof T, A> = {
  [Props in Key]: T[Props] | A;
};

type AppleAddNumber = AddTypes<A, 'apple', number>;

이렇게 AddTypes를 통해 A 인터페이스의 apple 프로퍼티에 number만 추가해서  프로퍼티가 하나인 AppleAddNumber 타입을 만들었다.

 

2번을 해보자 : 1번에서 뽑은 프로퍼티를 원본에서 제거한 타입을 정의한다.

interface A {
  /** 엄청난 길이의 주석 */
  apple: string
  ...
}

...

type AppleAddNumber = AddTypes<A, 'apple', number>;

type IDontHaveApple = Omit<A, 'apple'>

apple을 제외한 A에있는 모든 프로퍼티를 가진 IDonHaveApple 타입을 정의했다.

 

이제 둘을 합쳐보자: 3번: 둘을 합친다.

interface A {
  /** 엄청난 길이의 주석 */
  apple: string
  ...
}

...

type AppleAddNumber = AddTypes<A, 'apple', number>;

type IDontHaveApple = Omit<A, 'apple'>

type B = IDonHaveApple & AppleAddNumber;

이제 type B의 apple은 당초 원하던 대로 number만 가지게 된다.

 


아래는 코딩 스타일을 적용한 코드 샘플이다.

interface A {
  /** 주석1 */
  apple: string,
  /** 주석2 */
  banana: boolean,
  /** 주석3 */
  orange: string,
  ...
}

type AddTypes<T, extends keyof T, A> = {
  [Props in Key]: T[Props] | A;
};

type JoinType<T, K extends keyof T, A> = Omit<T, K> & AddTypes<T, K, A>;

type B = JoinType<A, 'apple'|'banana', number> ;
type C = JoinType<B, 'orange', boolean>;

//결과
type C = {
  /** 주석1 */
  apple: string | number,
  /** 주석2 */
  banana: boolean | number,
  /** 주석3 */
  orange: string | boolean,
  ...
}

 

댓글