My Boundary As Much As I Experienced

extends의 세 가지 용법 (인터페이스 확장, 조건부 타입, 제네릭 제약조건) 본문

FrontEnd/TypeScript

extends의 세 가지 용법 (인터페이스 확장, 조건부 타입, 제네릭 제약조건)

Bumang 2024. 5. 1. 00:49

타입스크립트에서 extends 키워드는 주로 두 가지 주요 용도로 사용된다.

인터페이스 확장 조건부 타입에서의 활용이다. 또한, 클래스를 상속할 때도 사용되지만, 여기서는 타입스크립트 관련 활용법만 다루겠다.

1. 인터페이스 확장

타입스크립트에서 extends를 사용하여 한 인터페이스가 다른 인터페이스를 확장할 수 있다. 이를 통해 인터페이스 간에 코드를 재사용하고, 계층적인 타입 정의를 구성할 수 있다. 

 

코드 재사용과 계층적인 타입 정의가 뭔지는 나중에 알아보고 일단 예시를 보겠다.

 

아래 Shape라는 interface는 sideLength라는 속성이 있다.

그리고 Square는 이를 상속받으면서 numberOfAngle라는 속성도 가지도록 'extends Shape'를 했다.

interface Shape {
  sideLength: number;
}

interface Square extends Shape {
  numberOfAngle: number;
}

let square: Square = {
  sideLength: 10,
  numberOfAngle: 4
};

 

이런 식으로 인터페이스를 확장해나갈 수 있다. Square라는 인터페이스를 처음부터 쌩으로 만들지 않고 이렇게 Shape로부터 확장시킴으로써 코드 사용량이 줄어든 것을 볼 수 있다. 이런 점이 코드를 재활용할 수 있다는 것이다.

 

그리고 Shape를 수정하면 Square에도 다시 추가될 수 있는 구조가 되었다. 그리고 Square가 Shape에 속하여 변의 길이(sideLength)를 가지고 있으면서도 추가적으로 각의 갯수(NumberOfAngle)도 4개인 걸 알 수 있다. 이는 실제 현실을 추상화했다고도 할 수 있다. 이렇듯 상호작용이 잘 보이며 추상화된 코드로 표현하는 것이 계층 구조를 만들어내는 것이라고 표현할 수 있다.

 

 

2. 조건부 타입

조건부 타입에서 extends 키워드는 타입 호환성을 확인하는 데 사용된다. 삼항연산자의 질문으로 '한 타입이 다른 타입에 할당 가능한지' 여부를 평가하고, 그 결과에 따라 다른 타입을 각각 반환한다.

 

이것도 말만 들으면 감이 안 올테니 예시를 보자.

type Check<T> = T extends string ? "Yes" : "No";
type Type1 = Check<string>;  // "Yes"
type Type2 = Check<number>;  // "No"

 

쉽게 생각해서 A extends B이면 후자B의 속성을 전자A가 가지고 있는지를 생각하라.

즉 T가 string인가? 맞으면 "Yes", 틀리면 "No"가 할당된다.

사실 string같은 원시타입이 extends 뒤에 있으면 "그 타입이냐?" 자체를 묻게 된다.

(반면 type이나 interface가 extends 뒤에 있으면, "이 속성들 가지고 있냐?"를 묻는거다.)

 

 

3. 제네릭 제약조건 (Generic Constraints)

extends를 사용하여 제네릭 타입에 제약조건을 추가할 수 있다. 이를 통해 제네릭 타입으로 받은 타입 파라미터가 특정 인터페이스를 구현하거나, 특정 타입을 상속하는 등의 조건을 만족해야 함을 명시할 수 있다.

 

쉽게 말해보겠다.

제네릭은 뭐든 될 수 있는 가능성의 타입이다. 그것이 extends 뒤에 있는 요소를 포함해야된다고 제약을 거는 것이다.

function logId<T extends { id: number }>(obj: T) {
  console.log(obj.id);
}

logId({ id: 10, name: "apple" });  // Works
logId({ name: "banana" });  // Error, 'id' property is missing

 

여기서 T extends { id: number }는 모든 T 타입이 최소한 { id: number } 형태를 가져야 함을 의미한다.

 

 

 

결론

세 가지 다 서로 다른 용법을 가지고 있지만 공통되는 개념은 아래로 정리할 수 있을거 같다.

'A extends B란, A에 B가 포함될 수 있냐? 즉 A가 B의 상위집합(super set)이냐?' 를 묻는다.