제네릭(Generics)
은 인터페이스, 클래스, 함수, 타입 별칭과 함게 사용할 수 있게 만들어진 기능입니다. 제네릭
은 타입을 미리 지정하지 않고 다양한 타입에 대응할 때 사용하게 됩니다.
제네릭
은 타입을 정의할때 기존에 사용하던 타입대신 T
라는 문자로 지정해 제네릭 타입
으로 정의합니다. 이때 문자 T
를 타입 변수라고 합니다.
interface A<T> {}
type B<T> {}
function C<T>(arg: T): T {}
class D<T> {}
애로우 함수(ES6+)
의 경우에는 제네릭 함수로 만드는 형태가 조금 다릅니다.const func = <T extends {}>(arg: T): T => {};
extends
키워드를 통해 해당 변수가 제네릭 애로우 함수라고 알리는 것을 명시해야합니다.
이렇게 제네릭을 선언하면 T
에는 호출 시점에 할당되는 타입이 결정됩니다. 함수같은 경우 매개변수로 숫자를 전달하면 호출때 T
가 number
타입으로 취급되겠죠?
그러면 실제로 제네릭 함수를 만들어서 호출을 해보고 어떻게 동작하는지 확인해보겠습니다.
첫 입문 과정에서 제네릭 애로우 함수보단 function 명령 함수가 가독성이 좋다고 판단하여 function 명령 함수를 이용했습니다.
다음과 같이 매개변수 arg를 받아서 그대로 arg를 반환하는 제네릭 함수
를 하나 생성했습니다.
function func<T>(arg: T): T {
return arg;
}
console.log(func(123));
console.log(func('hello'));
console.log(func(true));
console.log(func([1, 2, 3]));
함수에 타입을 지정하지 않았음에도 불구하고 다양한 타입(위 코드에서는 number, string, boolean, number[]
)에 대응하여 정상적으로 동작했습니다.
위 코드에서는 매개변수로 전달할 값의 타입에 따라 자동적으로 타입 추론을 해서 T의 타입이 결정되었습니다.
하지만 다음과 같이 꺾쇠를 이용해서 T의 타입을 명시적으로 전달할 수도 있습니다.
function func<T>(arg: T): T { return arg; } console.log(func<number>(123)); console.log(func<string>('hello')); console.log(func<boolean>(true)); console.log(func<number[]>([1, 2, 3]));
타입 변수가 두 개가 필요하다면 다음 코드처럼 두 개의 타입 변수를 사용합니다.
function func<T, U>(arg1: T, arg2: U) {} //반환값은 타입 추론에 맡김
배열이 단일 타입이 아닌 여러 타입이 섞여있을 경우에는 타입 변수가 다음과 같이 추론됩니다.
function func<T>(arg: T): T {
return arg;
}
func([123, 'hello']); // 타입 변수 T는 number | string 유니온 타입으로 추론
타입 변수가 될 수 있는 타입에 제약을 두어 타입을 한정시킬 수도 있습니다. 이때는 extends
키워드를 사용합니다.
function func<T extends {}>(arg: T): T {
return arg;
}
이때 extends
키워드 뒤의 중괄호{}
에 제약 조건을 넣으면 타입 변수 T는 해당 조건에 맞는 타입만을 받게 됩니다.
대표적으로 length
프로퍼티가 있는 값을 받도록만 해보면 결과는 다음과 같습니다.
function func<T extends {length: number}>(arg: T): T {
return arg;
}
console.log(func(123)); //Error!!!
console.log(func('hello')); //Success
console.log(func(true)); //Error!!!
console.log(func([1, 2, 3])); //Success
length 프로퍼티를 가질 수 있는 string, Array 객체
타입은 무사히 받아들이지만, 가지지 못하는 number, boolean
은 에러가 발생하게 됩니다.