타입 조작이란?
- 이미 할당 된 기본 타입, 별칭, 인터페이스 타입들을 새로운 타입으로 변환할 수 있는 타입스크립트 문법
타입 조작 기능
- 제너릭
- 인덱스드 엑세스 타입
- keyof 연산자
- Mapped(맵드) 타입
- 템플릿 리터럴 타입
- 조건부 타입
제너릭
- https://sillimmouse.tistory.com/75
인덱스드 엑세스 타입
- 객체, 배열, 튜플에서 특정 프로퍼티 혹은 요소의 타입을 추출하는 타입
- 대괄호 속에 들어가는 String Literal 타입인 “author” 를 인덱스라고 하며 인덱스를 이용해 특정 타입에 접근하다고 하여 인덱스드 엑세스 타입라고 한다.
- 인덱스에는 값이 아니라 타입(기본타입, 리터럴 타입 등)만 가능하다.
// 인덱스드 엑세스 타입
// 1. 객체 추출
interface Post {
title: string;
content: string;
author: {
id: number;
name: string;
// age: number; // 추가 시 post 변수, printAuthorInfo 함수 수정 필요
};
}
const post: Post = {
title: "게시글 제목",
content: "게시글 본문",
author: {
id: 1,
name: "이정환",
// age: 50
},
};
// function printAuthorInfo(author: { id: number; name: string }) {
// console.log(`${author.id} - ${author.name}`);
// }
function printAuthorInfo(author: Post["author"]) { // {id : number, name: string, age:number}
console.log(`${author.id} - ${author.name}`);
}
// 2. 배열 추출
type PostList = {
title: string;
content: string;
author: {
id: number;
name: string;
age: number;
};
}[];
const post2: PostList[number] = { // 배열의 요소 타입을 추출할 때에는 인덱스에 number 타입을 넣어주면 된다 [0] 리터럴 변수도 가능
title: "게시글 제목",
content: "게시글 본문",
author: {
id: 1,
name: "이정환",
age: 50
},
};
// 3. 튜플 타입
type Tup = [number, string, boolean];
type Tup0 = Tup[0]; // number
type Tup1 = Tup[1]; // string
type Tup3 = Tup[number] // number | string | boolean // number 타입을 넣으면 마치 튜플을 배열 처럼 인식해 배열 요소의 타입을 추출
keyof 연산자
- 특정 객체 타입으로 부터 프로퍼티 키들을 모두 스트링 리터렁 유니온 타입으로 추출하는 연산자
- keyof 연산자: 객체 타입으로부터 프로퍼티의 모든 key들을 String Literal Union 타입으로 추출하는 연산자, 오직 타입에만 적용할 수 있는 연산자이다.
- typeof 연산자: 자바스크립트에서 특정 값의 타입을 문자열로 반환하는 연산자로 타입 정의에 사용 시 타입추론의 기능을 할 수 있다.
// keyof 연산자
interface Person {
name: string;
age: number;
}
const person: Person = {
name: "kim"
age: 27,
};
// function getPropertyKey(person: Person, key: "name" | "age") {
// return person[key];
// }
function getPropertyKey(person: Person, key: keyof Person) {
return person[key];
}
getPropertyKey(person, "name");
// Typeof와 Keyof 함께 사용하기
type Person2 = typeof person;
function getPropertyKey2(person: Person, key: keyof typeof person) {
return person[key];
}
Mapped(맵드) 타입
- 기존의 객체타입으로 부터 새로운 객체를 만드는 타입
- 인터페이스로 만들수없고 type 만 가능하다.
// 맵드 타입
interface User {
id: number;
name: string;
age: number;
}
// type PartialUser = { // 중복 프로퍼티를 작성
// id?: number;
// name?: string;
// age?: number;
// }
type PartialUser = {
// [key in "id" | "name" | "age"]?: User[key];
[key in keyof User]?: User[key];
// readonly [key in keyof User]?: User[key];
};
function updateUser1(user: User) {
console.log(user)
}
function updateUser2(user: PartialUser) {
console.log(user)
}
// updateUser1({
// age: 25 // error - User의 id, name 없음
// });
updateUser2({
age: 25
});
템플릿 리터럴 타입
- 스트링 리터럴 타입을 기반으로 정해진 패턴의 문자열만 포함하는 타입
- 특정 패턴을 갖는 String 타입을 만드는 기능
// 템플릿 리터럴 타입
type Color = "red" | "black" | "green";
type Animal = "dog" | "cat" | "chicken";
// type ColoredAnimal = `red-dog` | 'red-cat' | 'red-chicken' | 'black-dog';
type ColoredAnimal = `${Color}-${Animal}`;
조건부 타입
- extend와 삼항 연산자를 이용해 조건에 따라 각각 다르게 정의하는 타입
// 조건부 타입 소개
// 1. 기본 타입
type A = number extends string ? number : string;
// 2. 객체 타입
type ObjA = {
a: number;
};
type ObjB = {
a: number;
b: number;
};
type B = ObjB extends ObjA ? number : string;
// 3. 제네릭 조건부 타입
type StringNumberSwitch<T> = T extends number ? string : number;
let varA: StringNumberSwitch<number>; // string
let varB: StringNumberSwitch<string>; // number
// 유니온 타입
// function removeSpaces(text: string | undefined | null) {
// if (typeof text === "string") {
// return text.replaceAll(" ", "");
// } else {
// return undefined;
// }
// }
// 조건부 타입
// function removeSpaces<T>(text: T): T extends string ? string : undefined {
// if (typeof text === "string") {
// // return text.replaceAll(" ", ""); // error - 함수 내부에서는 조건부 타입 결과를 알지 못함
// return text.replaceAll(" ", "") as any;
// } else {
// // return undefined; // error
// return undefined as any;
// }
// }
// 조건부 타입 + 오버로드 시그니쳐 추가
function removeSpaces<T>(text: T): T extends string ? string : undefined;
function removeSpaces(text: any) {
if (typeof text === "string") {
return text.replaceAll(" ", "");
} else {
return undefined;
}
}
let result = removeSpaces("hi im winterlood");
result.toUpperCase();
let result2 = removeSpaces(undefined);
분산적인 조건부 타입
- 조건부 타입에 유니온 타입을 사용 시 분산적인 조건부 타입으로 동작이 된다.
- 즉, 각각의 타입이 유니온으로 묶인 결과가 타입으로 지정되게 된다.
// 분산적인 조건부 타입
type StringNumberSwitch<T> = T extends number ? string : number;
let a: StringNumberSwitch<number>; // number
let b: StringNumberSwitch<string>; // string
let c: StringNumberSwitch<number | string>; // string | number
let d: StringNumberSwitch<boolean | number | string>; // string | number
// Exclude(제외하다) 조건부 타입 구현하기
type Exclude<T, U> = T extends U ? never : T;
type A = Exclude<number | string | boolean, string>; // number | boolean // never는 유니온타입 사용 시 타입에서 없어진다.
type Extract<T, U> = T extends U ? T : never;
type B = Extract<number | string | boolean, string>; // string
// 조건부 타입을 분산적으로 사용하고 싶지 않은 경우
// 타입에 [] 배열로 감싸주면 된다.
type Extract2<T, U> = [T] extends [U] ? T : never;
type C = Extract2<number | string | boolean, string>; // never // 첫번째 변수만 결과로 조회 됨
조건부 타입에서 타입 추론
- infer : Inference의 약자로 추론이라는 뜻
- 조건부 타입 작성 시 특정 타입을 지정해서 조건부를 작성하는 것이 아니라, 넘어오는 변수의 타입을 추론해서 조건부 타입결과를 조회하는 방법
// 조건부 타입에서 타입 추론
// infer
// type ReturnType<T> = T extends () => string ? string : never;
type ReturnType<T> = T extends () => infer R ? R : never;
type FuncA = () => string;
type FuncB = () => number;
type A = ReturnType<FuncA>; // string
type B = ReturnType<FuncB>; // number
type C = ReturnType<number>; // never
// Promise의 resolve 타입을 infer를 이용해 추출하는 예
type PromiseUnpack<T> = T extends Promise<infer R> ? R : never;
// 1. T는 프로미스 타입이어야 한다.
// 2. 프로미스 타입의 결과값 타입을 반환해야 한다.
type PromiseA = PromiseUnpack<Promise<number>>; // number
type PromiseB = PromiseUnpack<Promise<string>>; // string
출처
- 한 입 크기로 잘라먹는 타입스크립트(TypeScript)
'Frontend > TypeScript' 카테고리의 다른 글
[ERROR] React Hook names must start with the word 'use' react-hooks/rules-of-hooks (0) | 2024.05.07 |
---|---|
[TypeScript] TypeScript 유틸리티 타입 - 6 (0) | 2024.04.19 |
[TypeScript] TypeScript 인터페이스/클래스/제너릭 타입 이해 - 4 (0) | 2024.04.14 |
[TypeScript] TypeScript 함수 타입 이해 - 3 (0) | 2024.04.14 |
[TypeScript] TypeScript 타입 시스템 이해 - 2 (0) | 2024.04.14 |