→ 타입을 마치 함수의 파라미터처럼 사용하는 것.
function getText(text) {
return text;
}
getText('hi'); // 'hi'
getText(10); // 10
getText(true); // true
function getText<T>(text: T): T {
return text;
}
// 함수를 호출할 때, 아래와 같이 함수 안에서 사용할 타입을 넘겨줄 수 있다.
getText<string>('hi');
getText<number>(10);
getText<boolean>(true);
// getText<string>('hi'); 는 아래와 같이 타입을 정의한 것과 같다.
function getText<string>(text: string): string {
return text;
}
function logText(text: any): any {
return text;
}여러 타입을 허용하고 싶어 any를 사용할 경우, 함수의 동작에 문제가 생기진 않지만 함수의 인자로 어떤 타입이 들어갔고, 어떤 타입이 반환되었는지 알 수 없다.
any는 타입 검사를 하지 않기 때문이다.
이러한 문제점을 해결할 수 있는게 바로 **Generic**이다.
function logText<T>(text: T): T {
return text;
}
→ 함수의 이름 바로 뒤에 를 추가하고, 함수의 인자와 반환값에 T라는 타입을 추가하게 되면 함수를 호출할 때, 넘긴 타입에 대해 타입스크립트가 추정할 수 있게 된다.
따라서, 함수의 입력 값에 대한 타입과 출력 값에 대한 타입이 동일한지 검증할 수 있게 된다.
이렇게 선언한 함수는 2가지 방법으로 호출할 수 있다.
// #1
const text = logText<string>("Hello Generic");
// #2
const text = logText("Hello Generic");
/* 두번째 방법이 가독성, 코드 양 측면에서 좋기 때문에 많이 사용되다.
복잡한 코드에서 두번째 방법으로 타입 추정이 되지 않는다면 첫번째 방법을 사용하자 */
→
function sort<T>(items: []): T[] {
return items.sort();
}
const nums:number[] = [1, 2, 3, 4];
const chars: string[] = ["a", "b", "c", "d"];
sort<number>(nums);
sort<string>(chars);
class Queue<T> {
protected data: Array<T> = [];
push(item: T) {
this.data.push(item);
}
pop(): T | undefined {
return this.data.shift()
}
}
const numberQueue = new Queue<number>();
numberQueue.push(0);
numberQueue.push("1"); // Error, number타입이 아님
→ |를 사용해 두 개 이상의 타입을 선언하는 것.
const printMsg = (msg: string | number) => {
return msg;
}
const msg1 = printMsg(123);
const msg2 = printMsg("hello world");
msg1.length; // Error, number타입이기 때문에 에러가 발생함.
const printMsg = <T>(msg: T) => {
return msg;
}
const msg1 = printMsg<string>("hello world");
msg1.length; // 에러가 발생하지 않음.
→ 원하지 않는 속성에 접근하는 것을 막기 위해 제네릭에 제약조건을 사용한다.
→ 제네릭 T에 제약 조건을 설명한다(문자열 or 숫자)
제약 조건을 벗어나는 타입을 선언하면 에러가 발생한다.
const printMsg = <T extends string | number>(msg: T): T => {
return msg;
}
printMsg<Number>(123);
printMsg<String>("hello world");
printMsg<Boolean>(false); // Error, boolean 타입은 선언되어 있지 않기에 에러가 발생함.
→ 두 객체 값을 비교할 때 사용하는 제약조건.
const getProperty = <T extends object, U extends keyof T>(obj: T, key: U) => {
return obj[key]
}
getProperty({a: 1, b: 2, c: 3|, "a");
getProperty({a: 1, b: 2, c: 3|, "z"); // Error, "z"는 T의 키값에 존재하지 않기 때문에 에러가 발생함.
→ T는 오브젝트를 갖는 제약조건, U는 T를 갖는 제약조건을 가지고 있다.
제네릭 T는 키값이 a, b, c만 존재하는 오브젝트이기 때문에, U의 값인 ‘z’가 제네릭 T의 키 값 중 존재하지 않아 오류가 발생한다.
→ 객체를 생성하는 인터페이스만 미리 정의하고, 인스턴스를 만들 클래스의 결정은 서브 클래스가 내리는 패턴.

implements → class의 인터페이스에 만족하는지 여부를 체크할 때 사용.