본문 바로가기
개발공부

#8. 타입 조작하기

by 반류연 2025. 3. 5.

※ 이 글은 이정한 강사님의 '한 입 크기로 베어먹는 타입스크립트'를 수강한 뒤 정리한 글입니다.

 

 

타입 조작이란?

  • 원래 존재하던 타입들을 상황에 따라 유동적으로 다른 타입으로 변환하는 기능.
  • 제네릭을 포함하여 총 7가지 존재.

 

1. 인덱스드 엑세스 타입

  • 인덱스를 사용해 다른 타입내의 특정 프로퍼티 타입을 추출하는 타입.
  • 객체, 배열, 튜플에 사용 가능.
  • 인덱스 중첩 사용 가능.

※ 객체 프로퍼티 타입 추출

interface Post{
  title: string;
  content: string:
  author: {
    id: number;
    name: string;
  }
}

const post:Post = {
  title: "게시글 제목",
  content: "게시글 본문",
  author: {
    id: 1,
    name: "이정환"
  }
}


// 작성자 정보를 추출하는 함수
function printAuthorInfo(author: {id: number; name: string}){
  console.log(`${author.id} - ${author.name}`)
}

// 위 함수는 Post 인터페이스의 프로퍼티가 추가될때마다 수정해야하는 불편함 존재.
// -> 인덱스드 엑세스 타입 이용해 author의 프로퍼티 타입을 추출!
function printAuthorInfo(author: Post["author"]) {
  console.log(`${author.id} - ${author.name}`)
}

// 인덱스 중첩 사용도 가능
function printAuthorInfo(author: Post["author"]["id"]) {
  // author 매개변수의 타입은 number 타입이 됨
  console.log(`${author.id} - ${author.name}`);
}

 

  • 주의사항
    • 인덱스에는 타입만 들어갈 수 있음. 문자열값을 변수에 저장해서 인덱스처럼 쓰려고 하면 x
    • 인덱스에 존재하지 않는 프로퍼티 이름 쓰면 x
// 오류1
const authorKey = "author";

function printAuthorInfo(author: Post[authorKey]) {
  console.log(`${author.id} - ${author.name}`); 
}


// 오류2 
function printAuthorInfo(author: Post["what"]) {
  console.log(`${author.id} - ${author.name}`);
}

 

※ 배열 요소의 타입 추출

type PostList = {
  title: string;
  content: string;
  author: {
    id: number;
    name: string;
    age: number;
  };
}[];

const post: PostList[number] = {
  title: "게시글 제목",
  content: "게시글 본문",
  author: {
    id: 1,
    name: "이정환",
    age: 27,
  },
};

 

※ 튜플의 요소 타입 추출

type Tup = [number, string, boolean];

type Tup0 = Tup[0]; // number

type Tup1 = Tup[1]; // string

type Tup2 = Tup[2]; // boolean

type Tup3 = Tup[number] // number | string | boolean

 

 

2. keyof & typeof 연산자

  • 객체 타입으로부터 프로퍼티의 모든 key를 String Literal Union 타입으로 추출하는 연산자
  • 오직 타입에만 사용 가능
interface Person {
  name: string;
  age: number;
}

const person: Person = {
  name: "이정환",
  age: 27,
};

// 이렇게 만들면 Person 인터페이스에 프로퍼티 수정될때마다 함수 매개변수도 바꿔야 함
function getPropertyKey(person: Person, key: "name" | "age") {
  return person[key];
}


// -> keyof 연산자 사용해서 수정!
function getPropertyKey(person: Person, key: keyof Person) {
  return person[key];
}

 

 

3. 맵드 타입

  • 객체 타입을 기반으로 새로운 객체 타입을 만드는 타입 조작 기능.
interface User {
  id: number;
  name: string;
  age: number;
}

// 이렇게 쓰면 User 인터페이스와 중복되는 코드 많음.
type PartialUser = {
  id?: number;
  name?: string;
  age?: number;
}

// -> 맵드 기능 활용!
type PartialUser = {
  [key in "id" | "name" | "age"]?: User[key];
};

// -> 위 코드를 keyof 연산자를 이용해 한 번 더 수정
type PartialUser = {
  [key in keyof User]?: User[key];
};

function updateUser(user: PartialUser) {
  // ... 유저 정보 수정 기능
}

// 필요한 프로퍼티만 가져올 수 있음
updateUser({ 
  age: 25
});

 

4. 템플릿 리터럴 타입

  • 템플릿 리터럴을 이용해 특정 패턴을 갖는 String 타입을 만드는 기능
type Color = "red" | "black" | "green";
type Animal = "dog" | "cat" | "chicken";

// coloredAnimal : color 타입과 animal 타입을 조합해서 만들 수 있는 모든 가짓수. 
// 두 타입의 프로퍼티가 많아질수록 수정해야 됨.
type ColoredAnimal = `red-dog` | 'red-cat' | 'red-chicken' | 'black-dog' ... ;

// -> 템플릿 리터럴 타입 사용!
type ColoredAminal = `${Color}-${Animal}`;

 

5. 조건부 타입

  • extends와 삼항 연산자를 이용해 조건에 따라 각각 다른 타입을 정의하도록 돕는 문법.
type A = number extends string ? number : string;

// number 타입이 string 타입의 서브타입이니? -> 아니오
// 그러니 type A는 string 타입이 된다!
  • 제네릭과 함께 사용할 때 효율적!
type StringNumberSwitch<T> = T extends number ? string : number;

let varA: StringNumberSwitch<number>; // string
let varB: StringNumberSwitch<string>; // number

 

※ infer

  • 조건부 타입 내에서 특정 타입을 추론하는 문법
  • Return Type(특정 함수 타입에서 반환값의 타입만 추출하는 조건부 타입)을 만들때 이용
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

'개발공부' 카테고리의 다른 글

String.prototype.padStart()  (0) 2025.03.28
[공식문서 읽기/API] LocalStorage / Geolocation  (0) 2025.03.17
#7. 제네릭  (0) 2025.02.28
#6. 클래스  (0) 2025.02.27
#5. 인터페이스  (0) 2025.02.26