Generic
<T>
클래스, 함수, 인터페이스를 다양한 타입으로 재사용 가능
선언할 때는 타입파라미터만 적어주고, 생성하는 시점에 타입 파라미터를 결정
코드를 재사용하는데 아주 유용하게 사용된다
함수
function getSize(arr) : number {
return arr.length;
}
const arr1 = [1,2,3]; // 매개변수가 숫자 배열
getSize(arr1); //3
const arr2 = ["a","b","c"];//매개 변수가 문자열 배열
getSize(arr2);
다음과 같이 매개변수의 타입이 다른 경우
함수 오버로드 와 유니온 타입으로 해결
//함수 오버로드 + 유니온 타입
function getSize(arr :number[]): number;
fucntion getSize(arr : string[]: number;
function getSize(arr : number[] | string[]) : number {
return arr.length;
}
만약 계속 다른 타입이 늘어나게 된다면, 코드가 길어진다.
이것을 간단하게 제네릭으로 해결할 수 있다.
제네릭으로 해결
function getSize<T>(arr: T[]) : number { //type을 의미하는 <T>를 사용, T타입의 배열이 매개변수임을 작성
return arr.length;
}
const arr1 = [1,2,3]; // 매개변수가 숫자 배열
getSize<number>(arr1); // 사용하는 쪽에서 타입을 명시함
const arr2 = ["a","b","c"];//매개 변수가 문자열 배열
getSize<string>(arr2);
const arr3 = [false, false, true];//매개 변수가 boolean 배열
getSize<boolean>(arr3);
다음과 같이 <T>를 이용하여 함수를 정의한 후 해당 타입을 사용해서 매개변수의 타입을 임시로 지정해준다.
그 후 사용시점에 타입을 결정해준다.
인터페이스
인터페이스의 멤버변수 중 하나의 타입이 결정되지 않은 경우도 비슷하게 활용할 수 있다.
interface Mobile<T> {
name:string;
price: number;
option: T; //타입이 결정되지 않은 멤버변수
}
const m1: Mobile<object>{
name: "s21",
price: 1000,
option: { //object타입이 들어옴
color: "red",
coupon: false,
},
};
//참고로 object에 형식이 정해진 경우 이렇게도 가능하다
const m1: Mobile<{color : string; coupon:boolean}>{
name: "s21",
price: 1000,
option: { //object타입이 들어옴
color: "red",
coupon: false,
},
};
const m2: Mobile<string>{
name: "s21",
price: 1000,
option: "good", //string타입이 들어옴
};
제네릭에서의 extends의 활용
또한, extends를 이용하여, 입력으로 들어올 특정 타입T의 매개변수에 멤버변수등을 지정해줄 수 있다.
interface User {
name: string;
age:number;
}
interface Car {
name: string;
color : string;
}
interface Book {
price:number;
}
const user: User = {name: "Andrew", age:39};
const car: Car = {name: "bmw", color:"red"};
const book: Book = {price: 3000};
function showName<T extends {name: string}> (data:T): string{
return data.name;
}
showName(user);
showName(car);
showName(book); //book이라는 객체는 name을 가지고 있지 않으므로 에러
showName이라는 함수에 매개변수로 들어올 변수 data의 타입은 T 인데,
이 T타입은 name: string이라는 멤버를 보유하고 있어야 한다는 의미이다.
다양한 타입을 가진 객체가 매개변수로 올 수 있지만, name(string인)이라는 멤버를 보유하고 있는 객체만 올 수 있게된다.
유틸리티 타입
Utility Type
keyof
해당 타입이 가진 모든 멤버변수의 키값을 의미한다.
interface User{
id: number;
name: string;
age: number;
gender: "m"|"f";
}
type UserKey = keyof User; //'id' | 'name' | 'age' | 'gender' 와 같은 의미이다.
const uk:Userkey = "grade"; //error 존재하지 않는 멤버 키이므로
const uk2:Userkey = "id"; //ok
Partial<T>
프로퍼티를 전부 optional로 변경해준다.
interface User{
id: number;
name: string;
age: number;
gender: "m"|"f";
}
let admin: Partial<User> = { //age, gender가 빠졌지만 Partial<User>이므로 오류가 아님
id: 1,
name: "Bob",
}
즉 admin객체는 다음과 같은 인터페이스의 객체와 같다
interface User{
id?: number;
name?: string;
age?: number;
gender?: "m"|"f";
}
Required<T>
모든 프로퍼티를 필수로 변경해준다.
interface User{
id: number;
name: string;
age?: number;
gender?: "m"|"f";
}
let admin: Required<User> = { //age, gender도 필수가 되어 입력해주지 않으면 에러
id: 1,
name: "Bob",
age: 10,
gender : "f",
}
Readonly<T>
읽기 전용으로 변경하여, 처음 생성시를 제외하고는 수정이 불가능하다.
interface User{
id: number;
name: string;
age: number;
gender: "m"|"f";
}
let admin: Readonly<User> = {
id: 1,
name: "Shen",
};
let admin2: User = {
id: 2,
name: "Zed",
};
admin.name = "akali"; //error
admin2.name = "kenen"; //ok
Record<K,T>
key와 value 쌍을 지정할 수 있다.
학생의 학년별(1~4) 점수(A ~ F) 를 저장하는 오브젝트를 생성해본다.
interface Score {
"1" : "A" | "B" | "C" | "D" | "F";
"2" : "A" | "B" | "C" | "D" | "F";
"3" : "A" | "B" | "C" | "D" | "F";
"4" : "A" | "B" | "C" | "D" | "F";
}
const yjScore: Score = {
1: "B",
2: "B",
3: "A",
4: "A",
};
Record를 사용하지 않으면 다음과 같이 작성해야 한다.
인터페이스를 만들지 않고, Record를 이용해서 <key: 학년, value: 점수> 쌍으로 만들어보면,
const yjScore: Record<"1" | "2" | "3" | "4", "A" | "B" | "C" | "D" | "F"> = {
1: "B",
2: "B",
3: "A",
4: "A",
};
타입을 사용해서 조금 더 간단하게 정리해보면,
type Grade = "1" | "2" | "3" | "4";
type Score = "A" | "B" | "C" | "D" | "F";
const yjScore: Record<Grade, Score> = {
1: "B",
2: "B",
3: "A",
4: "A",
};
또한, 함수에도 이용할 수 있으며, 다음과 같이 응용해 볼 수 있다.
interface User {
id: number;
name: string;
age: number;
}
//해당 유저가 유효한 유저인지 확인하여 결과 object를 반환하는 함수
function isValid(user: User) {
const result: Record<keyof User, boolean> = {//User의 멤버 키 값, 유효한지(boolean)
id: user.id > 0,
name: user.name !== "",
age: user.age > 0,
};
return result;
}
Pick<T, K>
T 타입에서 K property만 골라서 가지고 있는 타입을 의미한다.
interface User {
id: number;
name: string;
age: number;
gender: "M" | "W";
}
const admin: Pick<User, "id" | "name"> = { //User 인터페이스에서 id, name멤버 만을 가진 타입
id: 0,
name: "Bob",
};
Omit<T,K>
Pick과 반대로 K 프로터피만을 제외하고 가진 타입을 말한다.
interface User {
id: number;
name: string;
age: number;
gender: "M" | "W";
}
const admin: Omit<User, "id" | "name"> = { //User 인터페이스에서 id, name멤버를 제외한 타입
age: 10,
gender : "M",
};
Exclude<T1, T2>
T1타입에서 T2"타입"과 겹치는 것을 제거하는 방식으로 Omit은 "프로퍼티"를 제외하므로 차이점이 있다.
type T1 = string | number | boolean;
type T2 = Exclude<T1, number | string>; // T2는 boolean만을 가짐
NonNullable<Type>
Null과 undefined을 제외한 타입
type T1 = string | null | undefined | void;
type T2 = NonNullable<T1>; //stirng, void만 가능
참고영상
https://www.youtube.com/watch?v=IeXZo-JXJjc&list=PLZKTXPmaJk8KhKQ_BILr1JKCJbR0EGlx0&index=8
'MATHrone > STUDY' 카테고리의 다른 글
TypeScript - 클래스, 접근제한자 (0) | 2022.01.24 |
---|---|
TypeScript - 함수, 리터럴, 유니온, 교차 타입 (0) | 2022.01.24 |
TypeScript - 기본타입, 인터페이스 (0) | 2022.01.21 |
[Error] cannot resolve symbol 'Routes' (0) | 2022.01.14 |