제네릭은 인터페이스에도 적용할 수 있다.
다음과 같이 인터페이스에 타입 변수를 선언해 사용하면 된다.
interface KeyPair<K, V> {
key: K;
value: V;
}
그리고, 다음과 같이 변수의 타입으로 정의하여 사용할 수 있다.
let keyPair: KeyPair<string, number> = {
key: "key",
value: 0,
};
let keyPair2: KeyPair<boolean, string[]> = {
key: true,
value: ["1"],
};
이때 주의해야 할 점은, 제네릭 인터페이스는 제네릭 함수와는 달리 변수의 타입으로 정의할 때 반드시 <>
(꺽쇠)와 함께 타입 변수에 할당할 타입을 명시해주어야 한다는 것이다.
그 이유는 제네릭 함수는 매개변수에 제공되는 값의 타입을 기준으로 타입 변수의 타입을 추론할 수 있지만 인터페이스는 마땅히 추론할 수 있는 값이 없기 때문이다.
제네릭 인터페이스는 인덱스 시그니쳐와 함께 사용하면 더욱 유연한 객체 타입을 정의할 수 있다.
// 인덱스 시그니쳐 사용 예
interface NumberMap {
[key: string]: number;
}
let numberMap: NumberMap = {
key: -1231,
key2: 123123,
};
// 제네릭 인터페이스와 인덱스 시그니쳐 함께 사용하기
interface Map<V> {
[key: string]: V;
}
let stringMap: Map<string> = {
key: "value",
};
let booleanMap: Map<boolean> = {
key: true,
};
인터페이스와 마찬가지로 타입 별칭에도 제네릭을 적용할 수 있다.
type Map2<V> = {
[key: string]: V;
};
let stringMap2: Map2<string> = {
key: "hello",
};
제네릭 타입 별칭을 사용할 때에도 제네릭 인터페이스와 마찬가지로 타입으로 정의될 때 반드시 타입 변수에 설정할 타입을 명시해 주어야 한다.
개발자 또는 학생이 이용하는 어떤 프로그램이 있다고 가정해보자.
// 타입 정의
interface Student {
type: "student";
school: string;
}
interface Developer {
type: "developer";
skill: string;
}
interface User {
name: string;
profile: Student | Developer;
}
// 유저 변수 생성
const developerUser: User = {
name: "Woodstock",
profile: {
type: "developer",
skill: "TypeScript",
},
};
const studentUser: User = {
name: "Shuroeder",
profile: {
type: "student",
school: "Peanuts School",
},
};
// 학생 유저만 가능한 기능
function goToSchool(user: User) {
if (user.profile.type !== "student") {
console.log("잘 못 오셨습니다.");
return;
}
const school = user.profile.school;
console.log(`${school}로 등교 완료`);
}
위 코드는 문제없이 잘 작동하는 것 같아 보인다. 그러나 만약, 학생만 할 수 있는 기능이 점점 많아진다면 기능을 만들기 위해 함수를 선언할 때 마다 조건문을 이용해 타입을 좁혀야 하고, 타입을 좁히는 코드는 중복 코드가 될 것이다.
이럴 때 제네릭 인터페이스를 이용하면 좋다.
interface Student {
type: "student";
school: string;
}
interface Developer {
type: "developer";
skill: string;
}
interface User<T> {
name: string;
profile: T;
}
function goToSchool(user: User<Student>) {
const school = user.profile.school;
console.log(`${school}로 등교 완료`);
}
const developerUser: User<Developer> = {
name: "Woodstock",
profile: {
type: "developer",
skill: "TypeScript",
},
};
const studentUser: User<Student> = {
name: "Shuroeder",
profile: {
type: "student",
school: "Peanuts School",
},
};
goToSchool(developerUser); // Error: 'User<Developer>' 형식의 인수는 'User<Student>' 형식의 매개 변수에 할당될 수 없습니다. 'school' 속성이 'Developer' 형식에 없지만 'Student' 형식에서 필수입니다.
goToSchool(studentUser);
그럼 이제 goToSchool
함수의 매개변수 타입을 User<Student>
처럼 정의해 학생 유저만 이 함수의 인수로 전달하도록 제한할 수 있다.
결과적으로 함수 내부에서 타입을 좁힐 필요가 없어지므로 코드가 훨씬 간결해진다.