일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- function 표현식
- vs code
- react router
- This
- type
- 함수 표현식
- 리액트 라우터
- function
- webstorm
- react-router
- BIND
- JavaScript
- 실행컨텍스트
- 객체
- Execution Context
- activation object
- Arrow function
- 함수
- moment.js
- 정적스코프
- hoisting
- 자바스크립트
- lexical environment
- scope chain
- function 문
- happy hacking
- 화살표 함수
- 호이스팅
- lexical scope
- variable object
- Today
- Total
Pandaman Blog
[Typescript] Utility Types - 1 본문
타입스크립트에서는 여러 유틸리티 타입을 제공한다. 이 유틸리티 타입은 Global 하게 사용가능하다.
이번 시간에는 유틸리티 타입의 종류와 왜 사용하는지? 어떻게 구현할 수 있는지 확인해 보려고 한다.
1. Partial
제네릭을 통해 넘겨받은 타입의 모든 속성을 optional로 변형해 반환하는 타입이다. 매우 간단하다.
interface Person {
name: string;
age: number;
}
function introduce(person: Partial<Person>) {
return `my name is ${person.name} and ${person.age} years old.`
}
introduce({ name: 'coo' });
introduce({ age: 33 });
introduce({ name: 'coo', age: 33 });
Person 타입의 속성은 옵션널하지 않지만, Partial를 통해 속성들은 옵션널한 타입으로 반환한다.
Partial은 어떻게 생겼을까? 한번 확인해보자.
type PartialTest<T> = {
[P in keyof T]?: T[P];
};
[1, 2, 3].map((P) => ...)
타입을 다양하게 사용하기 위해 제네릭<>
을 사용한다. 타입 매개변수을 T 로 정의한다. 첫번째로 [P in keyof T]
구문에 대해서 알아보자.
keyof operation 은 object 타입의 key 를 반환한다.
예제를 살펴보자.
링크
type Person = { name: string; age: number; 2022: 2022 };
type P = keyof Person;
// 'name' | 'age' | 2022
function getPerson(property: P) {
console.log(property);
}
getPerson('name');
getPerson('age');
getPerson(2022);
keyof 연산자를 통해서 해당 속성을 유니온 타입으로 반환한다. 즉 keyof T
의 의미는 타입 매개변수 T 속성의 속성들을 조합을 반환한다. 값이 아닌 속성을 타입으로 반환한다는걸 기억하자
돌아와서 in 키워드에 대해서도 알아보자.
링크
type Person = { name: string; age: number; };
type P = keyof Person; // 'name' | 'age'
type ResultPersonType = { [K in P]: Person[K] };
// { name: string; age: number }
in 키워드로 유니온 타입을 순회 한다고 한다. P 에 해당하는 name
과 age
가 순회한다.
K가 'name'
일때 Person['name']
은 string
이 되어 { name: string }
이 되고,
다음 K가 'age'
일때 Person['age']
은 number
이 되어 { age: nunmber }
가 된다.
{ name: string; age: number }
이제 [P in keyof T]
뒤에 있는 ?
만 알면 된다. 이미 우리는 알고 있다. 속성 뒤에 ?
는 옵션널 타입을 만들어 낸다는 것을..
type PartialTest<T> = {
[P in keyof T]?: T[P];
};
정리하자면, 제네릭으로 들어온 타입 매개변수 T(<T>
)의 속성을 유니온 타입으로 반환(keyof T
)하고 각 속성을 순회하여 (in
) [P]?: T[P]
를 만들어 낸다.
2. Required
requried 타입은 모든 속성을 필수 타입으로 지정한다.
interface OptionalType {
a?: number;
b?: number;
}
const optional: OptionalType = { b: 123 };
const required: Required<OptionalType> = { b: 123 };
// Property 'a' is missing in type '{ b: number; }' but required in type 'Required<OptionalType>'.
그렇다 옵션널인(?
) 타입을 모두 필수로 만들어준다. 예제를 살펴보면 Required<OptionalType>
로 인해 b 속성만 할당했을 경우 타입에러가 발생한다.Required
타입 내부는 어떻게 생겼을까.
type Required<T> = {
[P in keyof T]-?: T[P];
};
이제 우리는 제네릭(<T>
)을 알고, keyof
, in
에 대해서 알고 있다. 하지만 아직 모르는게 있다 바로 -?
이다. -
는 제거한다는 의미이다. 그리고 -?
는 ? 식별자를 제거한다는 의미이다. 따라서 모든 옵션널 타입(?
)은 제거되어 필수 타입이 되는것 이다. 사실 이키워드는 Mapped Type 에서만 사용할 수 있다.
3. ReadOnly
단어만 봐도 알 수 있다. 수정은 안되는 타입으로 반환한다.
interface Person {
name: string;
age: number
}
let person: Readonly<Person> = {
name: 'coo',
age: 20
};
person.name = 'I want to change my name';
// Cannot assign to 'name' because it is a read-only property.
변수 person에 할당한 값은 더이상 변경 할 수 없다. Readonly 타입은 아래와 같이 생겼다.
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
readonly 가 [P in keyof T]
앞에 붙는다. 여기서 위에서 배운 -
를 응용해보자. 어떻게 될까.
type RemoveReadonly<T> = {
-readonly [P in keyof T]: T[P];
};
let changablePerson: RemoveReadonly<Readonly<Person>> = {
name: 'coo',
age: 33
};
changablePerson.age = 20;
오.. Readonly<Person>
로 변경된 타입을 RemoveReadonly
의 타입매개변수로 전달했다. RemoveReadonly
는 -readonly
로 readonly
를 제거한다. 응용하면 점차 타입을 갖고 놀 수 있다.
4. Record
Record 타입은 두개의 타입 매개변수를 받는다. 첫번째 타입 매개변수는 속성이 되며, 두번째 매개변수는 속성 값의 타입이 된다.
한번 확인해보자.
링크
interface Person {
경력: number;
취미: string;
}
type FrontChapter = "bk" | "lio" | "blue" | "dali" | "coo";
const frontChapter: Record<FrontChapter, Person> = {
bk: { 경력: 10, 취미: "게임" },
lio: { 경력: 9, 취미: "독서" },
blue: {경력: 8, 취미: "축구"},
dali: { 경력: 7, 취미: "헬스" },
coo: { 경력: 6, 취미: "청소" },
};
FrontChapter 에 정의된 유니온 타입은 Record의 첫번째 타입인자에 할당되면 각각의 속성이 된다. Person 타입은 두번째 타입인자에 할당되며 각 속성의 값 타입으로 반환된다.
내부가 또 궁금하다.
type RecordTest<K extends keyof any, T> = {
[P in K]: T;
};
// K = 'bk' | 'lio' | ...
// T = { 경력: ..., 취미: ...}
// K = T
// bk: { 경력: ..., 취미: ...}
// lio: { 경력: ..., 취미: ...}
// ...
keyof any
가 보인다. 신경쓰이지만 한번 확인해보자. type AnyType = keyof any;
로 정의하고 마우스를 올려보자. type AnyType = string | number | symbol
이게 된다. K
는 string | number | symbol
을 extends 한 타입이라고 할 수 있다. 따라서 첫번째 타입 매개변수에는 string | number | symbol
의 유니온 타입으로 정의된 타입만 할당할 수 있다. 그리고 순회하여 P: T
을 만들어 낸다.
약간 복잡해졌지만 타입매개변수를 2개 받을 수 있다는점을 알게 되었고, extends 를 사용하여 원하는 타입매개변수의 타입을 한정시킬 수 있다는 점도 알 게 되었다.
5. Pick
Pick<Type, Keys>
은 단어를 유추해보면 알 수 있다. 타입 매개변수 Type에서 Keys에 해당하는 속성 타입을 Pick 하는것이다.
링크
type Person = {
name: string;
age: number;
job: string;
};
type PersonPreview = Pick<Person, 'name' | 'age'>;
// type PersonPreview = {
// name: string;
// age: number;
// }
첫번째 타입 매개변수는 Person 이고, 두번째 매개변수는 'name' | 'age'
로 Person의 속성을 타입으로 갖는다. 이제는 고민해서 Pick 유틸타입을 만들어보자.
첫번째 타입인자를 두개를 받는다. 두번째 인자는 첫번째 타입인자의 속성을 타입으로 갖는다.('name' | 'age'
)
그럼 아래까지 작성할 수 있겠다. 첫번째 타입인자의 속성을 타입으로 갖는다
은 keyof Type
로 표현가능하다. 그리고 extends
를 추가하여 keyof Type
를 확장한 타입이라고 명시하여 keyof Type
에 해당하는 타입만 두번째 타입인자로 지정할 수 있다.
type PickTest<Type, Keys extends keyof Type>
그리고 Keys는 순회되어야하며 { name: string; age: number }
형태로 만들어주어야 한다.name
과 age
는 Keys
로 즉 Keys = 'name' | 'age'
로 이루어져 있다.
type PickTest<Type, Keys extends keyof Type> = {
[Key in Keys]: any;
}
우리는 각 속성에 해당하는 타입 지정해야한다. 첫번째 타입 매개변수 Type을 생각해보자.
각 Key
로 Type
에 접근하게 되면 해당 Key
의 값의 타입을 도출할 수 있다.
ex) Key = 'age'
, Type = Person
, Type[Key] = Person['age']
type PickTest<Type, Keys extends keyof Type> = {
[Key in Keys]: Type[Key];
}
'Front end > Typescript' 카테고리의 다른 글
[Typescript] Utility Types - 2 (0) | 2022.03.27 |
---|---|
[Typescript] Conditional Types (0) | 2022.03.13 |
[Typescript] Mapped Types (0) | 2022.02.24 |
[Typescript] 함수 (0) | 2021.12.19 |
[Typescript] Type Guard (0) | 2021.12.18 |