Partial
Partial 유틸리티 타입의 사용용도는 다음과 같다. interface 를 전부 옵셔널로 만들어주게 된다.
interface Profile {
name: string;
age: number;
married: boolean;
}
// Partial 적용시
// interface Profile {
// name?: string;
// age?: number;
// married?: boolean;
// }
const zerocho: Profile = {
name: "zerocho",
age: 29,
married: false,
};
const newZeroCho: Partial<Profile> = {
name: "zerocho",
age: 29,
};
그럼 Partial 타입을 직접 만들어보자.
type P<T> = {
[Key in keyof T]?: T[Key];
};
keyof 와 in 에 대해 이해가 안된다면 다음글을 읽어보자.
Pick
아래의 예시에서 Pick 의 사용용도는 Profile 인터페이스에서 'name', 'age' 를 Pick 한다는 뜻이다.
const newZeroCho: Pick<Profile, "name" | "age"> = {
name: "zerocho",
age: 29,
};
즉, Pick 한 Profile 인터페이스는 다음과 같다.
interface Profile {
name: string;
age: number;
}
이제 직접이 Pick 을 타이핑 해보자.
type Pick<T, S extends keyof T> = {
[Key in S]: T[Key];
};
왜 S extends keyof T 를 써줘야했을까? 다음 처럼 interface 에 car 라는 key 는 존재하지 않는데 넣어주는 휴먼에러가 발생할 수 있기 때문이다. 따라서 S extends keyof T 를 통해 T 의 key 값들만 들어갈 수 있도록 제한해준 것이다.
interface Profile {
name: string;
age: number;
married: boolean;
}
type Pick<T, S extends keyof T> = {
[Key in S]: T[Key];
};
const newZeroCho: Pick<Profile, "name" | "age" | "car"> = {
name: "zerocho",
age: 29,
};
제네릭을 두개 이상 쓸 경우에 두 제네릭의 관계를 정의해주는 것이 중요하다. ex) extends
Omit
Omit 은 Pick 과 반대이다. interface Profile 에서 married 만 제외한 것이 된다.
const newZeroCho2: Omit<Profile, "married"> = {
name: "zerocho",
age: 29,
};
즉 Omit 한 인터페이스는 다음과 같아질 것이다.
interface Profile {
name: string;
age: number;
}
Omit 의 타입을 작성하기 위해서는 Exclude 를 알아야한다.
Exclude
keyof Profile 의 키값중에서 'married' 를 뺄 수 있다.
즉 Exclude 와 Pick 을 적절하게 조합하면 Omit 을 만들 수 있다.
type O<T, S extends keyof T> = Pick<T, Exclude<keyof T, S>>;
const newZeroCho2: O<Profile, "married"> = {
name: "zerocho",
age: 29,
};
Exclude
Omit 타이핑을 하면서 사용한 Exclude 또한 타이핑을 해보자. 생각해보려고 했는데 안 떠올라서 d.ts 를 참고했다.
type Exclude<T, U> = T extends U ? never : T;
다음과 같은 예제를 대입했는데 이해가 안됐다.
아래 예제 같은 경우에는 T = "name" | "age" | "married" 가 된다. 즉, U 보다 범위가 큰데 어떻게 부분집합이 되는지 이해가 되지 않았다.
알고보니 "name" , "age" , "married" 하나씩 U 에 부분집합에 성립하는지 체크하는 것이였다.
true 일 경우에는 never 라서 배제되고, false 라면 남게 된다.
interface Profile {
name: string;
age: number;
married: boolean;
}
type C = Exclude<keyof Profile, "married">;
key 이름 | T extends U 체크 | 남은 type |
"name" | false | "name" |
"age" | false | "name" | "age" |
"married" | true | "name" | "age" |
위 표는 Exclude 를 사용했을때 다음과 같은 타입이 나오는 근거가 된다.
Extract
Extract 는 interface에서 원하는 key 를 추출할 수 있다.
그럼 다음예제를 통해 Extract의 타이핑의 의미를 살펴보자.
interface Profile {
name: string;
age: number;
married: boolean;
}
type D = Extract<keyof Profile, "married" | "age">;
Extract가 정의된 타입은 다음과 같다.
type Extract<T, U> = T extends U ? T : never;
Exclude 와 반대인 모습이다. 표를 통해 확인해보자.
key 이름 | T extends U 체크 | 남은 type |
"name" | false | x |
"age" | true | "age" |
"married" | true | "age" | "married" |
Required
Required 의 사용용도는 다음과 같다.
타입이 다음처럼 모두 옵셔널로 선언된 경우가 생길 수 있다.
interface Profile {
name?: string;
age?: number;
married?: boolean;
}
Required 를 사용하게 되면 모두 말 그대로 required 가 된다. interface 에 선언된 타입을 지켜줘야한다.
interface Profile {
name?: string;
age?: number;
married?: boolean;
}
const newZeroCho: Required<Profile> = {
name: "zerocho",
age: 29,
married: false,
};
이제 이 Required 를 직접 타이핑을 해보자.
-? 에 주목할 수 있다. - 는 modifier 라고 하는데 ?(옵셔널)을 없앤다는 의미이다.
type R<T> = {
[Key in keyof T]-?: T[Key];
};
Readonly
객체를 수정할 수 없게한다. 다음 처럼 말이다.
Readonly 도 타이핑을 해보자.
interface Profile {
name: string;
age: number;
married: boolean;
}
type R<T> = {
readonly [Key in keyof T]: T[Key];
};
앞에 readonly 라는 예약어를 붙여주면 된다.
만약 interface 에 readonly 가 붙여있는 경우 위에서 배운 - 를 사용해서 뗄 수 있다.
interface Profile {
readonly name: string;
readonly age: number;
readonly married: boolean;
}
type R<T> = {
-readonly [Key in keyof T]: T[Key];
};
const newZeroCho: R<Profile> = {
name: "zerocho",
age: 29,
married: false,
};
newZeroCho.name = "DY";
Record
Record <string, number> 는 아래에 Obj interface 와 같은 기능을 한다.
interface Obj {
[key: string]: number;
}
const a: Record<string, number> = { a: 3, b: 5 };
Record 도 타이핑 해보자.
객체의 key 는 string, number, symbol 만 사용할 수 있기 떄문에 이 제한을 붙여주기 위해 extends keyof any를 사용하였다.
type R<T extends keyof any, S> = {
[Key in T]: S;
};
NonNullable
type 에서 null , undefined 인 type 을 제거 해준다.
NonNullable 을 타이핑하면 다음과 같다.
type N<T> = T extends null | undefined ? never : T;
or
type N<T> = T extends {} ? T : never;
나는 두번째 타이핑에서 T extends { } 가 어색했다. { } 는 null | undefined 를 제외한 타입들이다. 즉 따라서 null 과 undefined 는 위 조건에서 false 가 된다.
infer
Parameters
Parameters 라는 Utility Types 를 사용하면 아래의 zip 함수의 parameter 가 어떤 파라미터 타입을 가지는지 배열형식으로 타이핑이 된다.
First 라는 타입을 배열의 인덱스 0번에 접근하여 설정해준 모습이다.
Parameters 를 직접 타이핑 해보자. 이때 infer라는 예약어가 사용된다.
타이핑된 Parameters 는 다음과 같다.
type P<T extends (...args: any) => any> = T extends (...args: infer A) => any
? A
: never;
앞 부분의 제네릭 <T extends (...args: any) => any> 은 T 의 범위를 함수만 들어올 수 있게 제한하는 기능을 한다.
뒷부분의 T extends (...args: infer A) => any ? A : number; 을 살펴보자. ...args 즉 파라미터 자리에 infer A 가 위치한다.
이 코드의 의미는 파라미터의 타입을 타입스크립트가 추론할 수 있다면 그 추론한 타입을 사용하고, 아니라면 never 를 사용하도록 하는 것이다.
infer의 위치를 변경해주면 함수의 return 타입도 알 수 있다.
type P<T extends (...args: any) => any> = T extends (...args: any) => infer A
? A
: never;
ConstructorParameters
ConstructorParameters의 타입은 다음과 같다.
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;
다음 제네릭 부분을 볼 수있다. <T extends abstract new (...args: any) => any> 이 코드의 의미는 T 의 범위를 class 로 제한 한다는 것이다.
T extends abstract new (...args: infer P) => any ? P : never; 뒷 부분의 의미는 다음과 같다. ...args 안에 infer P 가 있으므로 이는 class 의 생성자의 타입을 추론할 수 있다면 그 타입을 반환하는 것이다.
InstanceType
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
InstanceType 도 ConstructorParameters의 타입과 비슷하지만 뒷부분이 다르다.
T extends abstract new (...args: any) => infer R ? R : any; infer R 부분이 instance 를 의미한다. 따라서 instance 의 타입을 추론할 수 있다면 그 타입을 사용한다.
instance 의 타입으로 class 의 이름인 A를 얻을 수 있다.
'공부기록 > 웹 개발' 카테고리의 다른 글
[우아한 테크코스 프리코스] 2주차 (1) | 2022.11.09 |
---|---|
html dataset 을 사용한 css switch case (1) | 2022.10.11 |
[타입스크립트] keyof , in keyof (0) | 2022.10.06 |
[타입스크립트] 자주쓰는 메소드 타이핑 (1) | 2022.10.06 |
[타입스크립트] class (0) | 2022.10.05 |