제네릭(Generics)

김동현·2022년 3월 29일
1

TypeScript

목록 보기
15/18
post-thumbnail

내장 제네릭 타입

Array<type>

우리는 배열의 타입 지정할 때 : type[]을 작성하여 타입을 지정했습니다.

타입 추론 역시 type []으로 추론되는 것도 앞에서 살펴보았습니다.

이때 배열 타입을 선언할 때 또 다른 방법을 사용할 수 있습니다. 바로 내장된 제네릭 타입인: Array<type>을 사용할 수 있습니다.

: Array만 작성하면 아래처럼 제네릭 형식이라고 표시되며 하나의 타입 인수가 필요하다고 에러가 표시되고 있습니다. 이때 타입 인수는 <type>을 의미합니다. 여기서 타입 인수는 배열이 갖는 요소의 타입을 의미합니다.

제네릭 타입은 다른 타입과 연결되는 타입인데 이때 연결되는 다른 타입에 대해서 어떤 타입이 되든지 크게 상관하지 않습니다.

배열 타입의 경우에는 배열 타입과 배열의 요소 타입이 서로 연결됩니다. 즉, 배열 타입의 경우 최소한 배열의 요소가 어떤 타입을 가지는지에 대해서 타입스크립트에게 알려주어야 하며 요소는 어떤 타입이든 가질 수 있습니다.

Array<type>type[]과 완전히 동일하며, 이는 내장된 제네릭이라고 합니다. 예를 들어, Array<string>string[]은 동일하며 타입스크립트에게 해당 변수는 배열 타입이며 배열의 요소 타입은 string 타입이라는 것을 알려줍니다. 배열 타입과 배열 요소의 타입을 알려줌으로써 우리가 배열이나 배열의 요소에 접근할 때 관련된 API를 자동완서으로 표시해주며, 이외 다른 프로퍼티나 메서드에 접근시 에러를 표시해줍니다.

이처럼 주요 타입은 프로미스 타입이지만 배열처럼 다른 타입과 함께 동작합니다. 배열은 특정 타입의 데이터를 저장하기 때문에 다른 타입과 함께 동작하고, 프로미스는 결국 어떤 타입의 데이터를 반환하는 것으로 다른 타입과 함께 사용됩니다.

이처럼 제네릭 타입을 사용하면 보다 나은 타입 안정성을 확보할 수 있으며 제네릭 타입을 사용하여 작업을 할 때마다 타입스크립트의 도움을 받고자 한다면 입력되는 데이터 타입 정보를 저장하여 타입스크립트에게 알려주어야 합니다.

Promise<type>

또다른 내장된 제네릭 타입으로는 프로미스가 있습니다.

위 코드처럼 Promise 생성자 함수로 프로미스를 생성합니다. 이때 프로미스 객체를 할당받는 promise 변수의 타입이 Promise<unknown>으로 추론된 것을 확인할 수 있습니다.

프로미스는 비동기 처리 결과에 대한 타입을 지정하는데 이때 <unknown>부분이 바로 비동기 처리 결과에 대한 타입이 됩니다.

위 코드에서는 resolve한 값이 문자열이므로 명시적으로 Promise<stirng>으로 작성하여 프로미스 타입을 지정하여 타입스크립트에게 promise 변수의 타입은 프로미스 타입이며 비동기 처리 결과의 타입 또한 문자열인 것을 알려줌으로써 접근시 관련된 API 자동완성과 접근할 수 없는 프로퍼티나 메서드에 대한 에러를 표시해줍니다.

제네릭 타입 사용하기

제네릭 타입은 타입 변수로 볼 수 있습니다. 즉, 타입을 전달받는 변수입니다.

  • 제네릭 타입입은 하나의 타입으로 고정되어있지 않습니다.

  • 제네릭 타입의 타입은 런타임에 결정됩니다.

제네릭 타입은 함수, 클래스, 타입 별칭, 인터페이스에서 사용 가능합니다.

제네릭 타입은 기존처럼 런타임 이전 하나의 특정 타입으로 지정하는 것이 아니라 런타임에 다양한 타입으로 지정될 수 있는 타입입니다.

제네릭 타입은 변수명이나 함수명 뒤에 "<T, U, K, ,,,>"처럼 선언하여 사용합니다.

제네릭 함수

함수의 경우 함수 이름(변수 이름) 뒤에 <T, U, K,,, >로 제네릭 타입을 작성할 수 있습니다.

작성된 각 제네릭 타입은 함수의 매개변수, 함수 몸체 내부에서 "타입처럼 사용"할 수 있습니다.


위 코드처럼 제네릭 타입 T와 U를 매개변수 a와 b의 타입으로 명시했습니다.

제네릭 타입들은 런타임 이후 전달받는 인수의 각 타입이 T와 U로 설정되며, 반환값 타입 또한 정확하게 추론되는 것을 확인할 수 있습니다.

혹은 명시적으로 제네릭 타입을 전달할 수도 있습니다.

위 코드처럼 함수 호출문 작성할 때 "함수이름<type, type,,,>(인수,,);"로 작성하여 제네릭 타입에 명시적으로 타입을 전달할 수도 있습니다.

extends

제네릭 타입에 extends를 사용하여 제약 조건을 작성할 수 있습니다.

위에서 본 merge 함수의 제네릭 타입 T와 U는 실제로 모든 타입의 값을 전달받을 수 있습니다. 이를 extends 키워드로 제네릭 타입의 범위를 제한할 수 있습니다.

제네릭 타입 T와 U의 타입을 extends object 작성하여 object의 서브 타입만을 전달받도록 하였습니다.

참고로 ,(콤마)를 작성하여 여러 타입 작성이 가능합니다.

제네릭 클래스

클래스에서도 제네릭 타입을 사용할 수 있습니다. 동일하게 클래스 이름 옆에 "<T, U, K,,,>"를 작성하여 사용할 수 있습니다.

작성된 제네릭 타입은 클래스 몸체에서 타입처럼 사용할 수 있습니다.

클래스를 new 연산자와 함께 호출할 때 제네릭 타입에게 타입을 명시적으로 전달할 수 있습니다.

제네릭 타입 별칭

type 키워드로 타입 별칭을 사용하는 경우에도 제네릭 타입을 사용할 수 있습니다.

타입 이름 옆에 <T, U, K,,,>를 작성하여 사용할 수 있습니다.

주의할 점으로 타입 별칭에 제네릭 타입을 사용하는 경우 반드시 제네릭 타입에 타입을 명시적으로 전달해주어야 합니다.

위 코드처럼 작성된 모든 제네릭 타입에 타입을 명시적으로 전달해주어야 합니다

제네릭 인터페이스

interface 키워드로 생성한 인터페이스에서도 제네릭 타입을 사용할 수 있습니다.

인터페이스 이름 옆에 <T, U, K,,,>를 작성하여 사용할 수 있습니다.

주의할점으로 타입 별칭과 마찬가지로 반드시 제네릭 타입에 타입을 명시적으로 전달해주어야 합니다.

조건부 타입

제네릭 타입을 통해 조건부 타입을 작성할 수 있습니다. 조건부 타입은 자바스크립트의 삼항 연산자 처럼 사용할 수 있으며, 제네릭 타입에 따라 타입이 결정되도록 합니다.

T extends type1 ? : type2 : type3 형태로 사용하며, 제네릭 타입 T가 type1의 서브 타입인 경우 type2이며, 아닌 경우 type3로 결정됩니다.

제네릭 타입 T가 string 타입의 서브 타입인 경우에는 string[]이고, 아닌 경우에는 number[]타입이 됩니다.

제네릭 유틸리티 타입

Partial<T>

Parial 제네릭 타입은 T의 모든 프로퍼티들을 "옵셔널 프로퍼티"로 갖는 새로운 객체 타입을 반환합니다.

Partial<User>을 타입으로 지정된 변수들은 User 인터페이스 지정된 프로퍼티를 옵셔널 프로퍼티로 갖는 객체 타입이 됩니다. 즉, 타입스크립트는 UserInfo 타입에 대해서 name과 age 프로퍼티를 선택적으로 가질 수 있다고 인지하도록 작성한 것입니다.

Readonly<T>

Readonly 제네릭 타입은 배열의 경우 readonly 배열 타입을 생성하고, 일반 객체의 경우 모든 프로퍼티가 readonly인 객체 타입을 생성하게 됩니다.

즉, as const 단언문처럼 동작하게 됩니다.

readonly 배열 타입의 경우에는 요소값 읽기만 가능하며 요소값 변경, length 값 변경, 원본 배열 변경하는 메서드 호출 등 모두 불가능한 배열 타입이 됩니다.

객체의 프로퍼티가 readonly인 경우 해당 프로퍼티 값 읽기만 가능하며, 변경하거나 제거하는 것은 불가능해집니다.
참고로 프로퍼티 값의 타입 또한 const 키워드 변수처럼 타입이 추론됩니다.

Pick<T, Key>

Pick 제네릭 타입은 매핑된 타입을 사용한 새로운 객체 타입을 생성하게 됩니다.

즉, 기존 객체 타입의 일부 프로퍼티만 선택된 새로운 객체 타입을 반환합니다.

이때 Key 제네릭 타입은 반드시 T의 키 값만 전달해야 합니다.

Omit<T, Key>

Omit 제네릭 타입은 Pick 제네릭 타입과 반대되는 개념입니다. 기존 객체 타입의 일부 프로퍼티만 제거된 새로운 객체 타입을 반환하게 됩니다.

동일하게 Key 제네릭 타입으로는 T의 키 값만 전달해주어야 합니다.

위 코드에서는 Person 인터페이스에서 score 프로퍼티만 제외한 새로운 객체 타입을 생성하였습니다.

ReturnType<T>

ReturnType 제네릭 타입은 함수의 반환값 타입을 새롭게 생성합니다.

즉, ReturnType<함수타입>처럼 제네릭 타입으로 함수 타입을 전달해주어야 합니다.

Parameters<T>

Parameters 제네릭 타입은 함수의 매개변수를 인덱스로 갖는 튜플 타입을 생성합니다.

즉, Parameters<함수타입>처럼 제네릭 타입으로 함수 타입을 전달해주어야 합니다.

ArrayLike<T>

ArrayLike 제네릭 타입은 유사 배열 객체 타입을 생성해줍니다. 즉, length 프로퍼티와 인덱스를 갖는 객체 타입을 생성해줍니다.

ArrayLike<요소타입> 형식으로 사용하며 반드시 length 프로퍼티를 가져야 하고, 지정된 타입의 요소값만을 가질 수 있습니다.

제네릭 타입으로 요소 타입을 전달해주어야 합니다

Record<Key, Type>

Record 제네릭 타입은 객체 타입을 반환합니다. Recodr<Key, Type>에서 Key에는 프로퍼티 키 값, Type에는 프로퍼티 값의 타입을 전달해줍니다.

Record 제네릭 타입은 객체의 프로퍼티들을 다른 타입으로 매핑시킬 때 사용됩니다.

위 코드에서는 Key에 키 타입을 의미하는 유니온 타입을 전달하고, Type으로 number 타입을 전달하여 모든 프로퍼티 값의 타입을 number로 갖는 객체 타입을 생성하였습니다.

Exclude<Type1, Type2>

Exclude 제네릭 타입은 Type1 유니온 타입에서 Type2 유니온 타입을 제외한 유니온 타입을 반환합니다.

위 코드에서는 'a' | 'b' | 1 | 2 유니온 타입에서 1 | 2 유니온 타입을 제외한 'a' | 'b' 유니온 타입을 새롭게 생성하였습니다.


이외 여러 가지 유틸리티 제네릭 타입이 존재하며 여기에서 살펴볼 수 있습니다.

profile
Frontend Dev

0개의 댓글