* 조시 골드버그의 러닝 타입스크립트라는 책을 읽고 정리하면서 코드를 작성해본 내용입니다.
타입스크립트를 사용할 때 항상 타입을 선언할 수 있는것은 아니다.
예를 들어 다음과 같은 함수가 있을 때
매개변수의 타입에 따라 반환값이 달라지고, 어떤 매개변수가 들어올지 모르기 때문에 any로 설정할 수 밖에 없다. 이러한 경우 타입검사기의 보호를 받을 수 없다.
input 타입과 함수 반환 타입간의 관계를 말할 수 있는 방법이 필요한데, 타입스크립트에서는 제네릭(Generic)을 사용하여 타입간의 관계를 나타낼 수 있다.
제네릭 타입 매개변수는 제네릭 구조체의 사용법에 따라 타입이 결정된다.
<T> 와같은 방식으로 제네릭을 선언할 수 있다.
1. 제네릭 함수
매개변수 괄호 앞에 < > 사이에 타입매개변수 별칭을 넣으면 함수를 제네릭으로 만든다. 이렇게 하면 해당 함수 안에서 제네릭을 타입 애너테이션으로 사용할 수 있다.
* 화살표 함수도 마찬가지로 매개변수 괄호 앞에 제네릭을 선언하면 되지만, .tsx 파일에서 jsx 구문과 충돌하기 때문에 일부 제한이 있다. 이 경우 구성옵션을 변경하여 해결한다.
이렇게 선언을 하면 매개변수 값에 따라 타입이 정해지고, 타입 검사기의 보호를 받을 수 있다.
1.1 명시적 제네릭 호출 타입
위의 함수는 callback 함수를 인자로 받는데, callback 함수의 인자는 제네릭인 <Input> 타입을 인자로 받는다. 그러나 매개변수 타입을 모르는 경우 타입스크립트는 제대로된 유추를 할 수 없다.
이러한 경우 명시적으로 제네릭을 선언함으로써 콜백함수의 매개변수의 타입을 명시적으로 지정해 줄 수 있다.
명시적 타입 인수 지정은 필요할 때만 사용할 것을 권장한다.
1.2 다중 함수 타입 매개변수
제네릭을 여러번 지정할 수 있다.
여러개의 제네릭이 있을 때 명시적 제네릭 호출타입을 설정하고 싶으면 모든 제네릭에 대해 설정하거나, 모두 설정하지 않아야 한다.
2. 제네릭 인터페이스
인터페이스에서도 제네릭을 선언할 수 있다. 인터페이스 이름뒤에 <>로 선언한다.
* 타입스크립트 내장 Array 메서드는 제네릭 인터페이스로 정의된다.
2.1 유추된 제네릭 인터페이스 타입
제네릭 인터페이스 역시 타입스크립트가 자체적으로 유추할 수 있다.
유추된 제네릭에 맞지 않는 타입을 할당하면 에러가 발생한다.
3. 제네릭 클래스
클래스도 마찬가지로 맴버에게 사용할 제네릭을 선언할 수 있다.
3.1 명시적 제네릭 클래스 타입
기본적으로 전달되는 매개변수에 따라 제네릭 타입을 유추한다.
타입을 유추하지 못하는 경우 기본값은 unknown이다.
위에서 본 함수 유사하게 명시적으로 제네릭을 선언할 수 있다.
3.2 제네릭 클래스 확장
제네릭은 extends로 확장한 클래스에도 사용가능하다. 상위 클래스에 대한 제네릭 타입은 유추하지 않기 때문에 명시적으로 지정해줘야 한다.
제네릭을 지정하지 않으면 에러가 발생한다.
3.3 제네릭 인터페이스 구현
제네릭 인터페이스를 선언해서 클래스에서 확장하듯이 사용할 수 있다. 이 때 인터페이스 내의 모든 프로퍼티는 클래스 내에서 선언되어야 한다.
3.4 메서드 제네릭
클래스 메서드는 인스턴스와 별개로 자체 제네릭 타입을 선언할 수 있다.
3.5 정적 클래스 제네릭
클래스의 정적(static) 맴버는 자체 제네릭 매개변수를 선언할 수 있지만, 클래스에 선언된 다른 어떤 타입 매개변수에도 접근할 수 없다.
4. 제네릭 타입 별칭
타입 별칭에도 제네릭을 사용할 수 있다. 주로 제네릭 함수의 타입을 설명할 때 사용한다.
4.1 제네릭 판별된 유니언
판별된 유니언과 제네릭을 조합하여 타입 별칭으로 사용할 수 있다.
위와같이 데이터를 받는데 성공과 실패에 따라 다르게 처리하는 함수를 만들 때 유용하다.
5. 제네릭 제한자
5.1 제네릭 기본값
제네릭을 선언할 때 선언뒤에 =와 기본 타입을 배치해 기본값을 명시적으로 제공할 수 있다.
기본타입이 있는 제네릭 매개변수는 기본 함수 매개변수처럼 마지막에 와야 한다.
6. 제한된 제네릭 타입
함수에서 제네릭을 선언할 때 interface를 확장하여 선언할 수 있다.
위의 경우 input은 length라는 프로퍼티를 가지는 어떤 값이든 사용할 수 있다.
length를 가지는 문자열, 배열은 물론이고, length라는 프로퍼티를 가지는 객체도 사용 가능하다. 이외의 값을 할당하면 에러가 발생한다.
6.1 keyof와 제한된 타입 매개변수
extends와 keyof를 함께 사용하면 타입 매개변수를 이전 타입 매개변수의 키로 제한할 수 있다. 이것은 제네릭 타입의 키를 지정하는 유일한 방법이다.
7. Promise
7.1 Promise 생성
타입스크립트에서의 Promise는 위와 유사하게 생겼다.
값을 resolve하려는 Promise를 만들려면 Promise의 타입 인수를 명시적으로 선언해야 한다. 그렇지 않으면 unknown 타입으로 이해한다.
.then 메서드는 반환되는 Promise의 resolve된 값을 나타내는 새로운 타입 매개변수를 받는다.
7.2 async 함수
async 함수는 Promise를 반환한다. Promise가 아닌값을 반환해도 Promise로 만들어서 반환한다.
따라서 async 함수가 특정 타입의 값을 반환할 때 타입스크립트는 반환값을 Promise<타입>으로 유추한다.
8. 제네릭 올바르게 사용하기
8.1 제네릭 황금률
함수에서 타입 매개변수가 두 번 이상 사용된다면 사용하고, 아니라면 사용하지 않는 것을 권장한다.
8.2 제네릭 명명 규칙
첫 번째 타입 인수는 T를 사용하고, 후속 타입 인수가 필요하면 U, V 등을 사용한다.
타입 인수가 어떻게 사용될지 알고 있다면 해당 용어의 첫 글자를 사용한다. 예를 들어 상태 관리 라이브러리에서는 제네릭 상태를 S로, 데이터 구조의 키와 값은 K, V로 나타낸다.
제네릭의 의도가 단일 문자로 명확하게 알 수 없다면 단일 문자 대신 완전한 이름을 사용하기도 한다.
Reference
- 조시 골드버그, 러닝 타입스크립트, 고승원 옮김, 2023
- https://www.typescriptlang.org/ko/docs/handbook/intro.html
'개인공부 > Typescript 공부' 카테고리의 다른 글
Typescript 타입 제한자 (0) | 2023.03.11 |
---|---|
Typescript 클래스 (0) | 2023.03.09 |
Typescript 인터페이스 (0) | 2023.03.05 |
Typescript 배열 (0) | 2023.03.03 |
Typescript 함수 (0) | 2023.03.01 |
댓글