TypeScript - 유틸리티 타입

안녕하세요, 미래의 코딩 마법사 여러분! 오늘 우리는 TypeScript의 마법적인 세계, 유틸리티 타입의 흥미로운 여정을 시작할 것입니다. 프로그래밍에 새로운 사람이라면 걱정하지 마세요; 저는 당신의 친절한 안내자가 되겠습니다. 우리는 이 개념들을 함께 단계별로 탐구해 나갈 것입니다. 그럼, 가상의 지팡이(키보드)를 손에 들고, 함께 뛰어들어보겠습니다!

TypeScript - Utility Types

유틸리티 타입이란?

시작하기 전에 유틸리티 타입이 무엇인지 이해해보겠습니다. 상상해보세요. 다양한 도구가 가득한 도구 상자가 있습니다. 각 도구는 특정 작업을 더 효율적으로 수행하는 데 도움을 줍니다. TypeScript의 유틸리티 타입도 exactamente 그와 같습니다. 이들은 미리 만들어진 도구들로, 타입을 쉽게 조작하고 변환하는 데 도움을 줍니다.

이제 이 마법의 도구들을 하나씩 살펴보겠습니다!

TypeScript에서의 Partial 타입

Partial 타입은 객체의 모든 프로퍼티를 선택 사항으로 만드는 마법 같은东西입니다. 모든 프로퍼티를 지정할 필요 없는 객체를 만들고 싶을 때 매우 유용합니다.

다음은 그东西의 동작을 보여줍니다:

interface Wizard {
name: string;
age: number;
house: string;
}

function updateWizard(wizard: Wizard, fieldsToUpdate: Partial<Wizard>) {
return { ...wizard, ...fieldsToUpdate };
}

const harryPotter: Wizard = {
name: "Harry Potter",
age: 11,
house: "Gryffindor"
};

const updatedHarry = updateWizard(harryPotter, { age: 17 });
console.log(updatedHarry);
// 출력: { name: "Harry Potter", age: 17, house: "Gryffindor" }

이 예제에서 Partial<Wizard>는 Harry의 나이를 업데이트할 때 모든 다른 프로퍼티를 지정할 필요 없이 사용할 수 있게 해줍니다. 마치 "Partial Revelio!"라고 외치는 것과 같습니다!

TypeScript에서의 Required 타입

Required 타입은 Partial의 반대입니다. 모든 프로퍼티를 원래 선택 사항이었더라도 필수로 만드는 마법 같은东西입니다.

interface MagicalCreature {
name: string;
power?: string;
age?: number;
}

const dragon: Required<MagicalCreature> = {
name: "Norwegian Ridgeback",
power: "Fire Breath",
age: 2
};

// 이东西는 오류를 발생시킨다:
// const unicorn: Required<MagicalCreature> = {
//   name: "Silver Horn"
// };

여기서, 원래 인터페이스에서는 선택 사항이었던 powerage가 Required 타입에 의해 필수로 만듭니다. 마치 "Accio all properties!"라고 외치는 것과 같습니다!

TypeScript에서의 Pick 타입

Pick 타입은 기존 타입에서 특정 프로퍼티를 선택하여 새로운 타입을 만드는 것입니다. 마치 특정 프로퍼티만 호출하는 소환 마법을 사용하는 것과 같습니다.

interface Potion {
name: string;
ingredients: string[];
brewingTime: number;
effect: string;
}

type PotionLabel = Pick<Potion, 'name' | 'effect'>;

const polyjuicePotion: PotionLabel = {
name: "Polyjuice Potion",
effect: "Transforms the drinker into another person"
};

이 예제에서, 우리는 Potion 인터페이스에서 nameeffect 프로퍼티만 포함하는 새로운 타입 PotionLabel을 만들었습니다. 특정 세부 정보가 필요할 때 완벽합니다!

TypeScript에서의 Omit 타입

Omit 타입은 Pick의 반대입니다. 기존 타입에서 특정 프로퍼티를 제거하여 새로운 타입을 만듭니다. 마치 특정 프로퍼티에 대해 사라지는 마법을 사용하는 것과 같습니다!

interface SpellBook {
title: string;
author: string;
pages: number;
secretSpell: string;
}

type PublicSpellBook = Omit<SpellBook, 'secretSpell'>;

const beginnerSpellBook: PublicSpellBook = {
title: "Standard Book of Spells, Grade 1",
author: "Miranda Goshawk",
pages: 250
};

여기서, 우리는 secretSpell을 제외한 SpellBook의 모든 프로퍼티를 포함하는 PublicSpellBook 타입을 만들었습니다. 마치 "Show me everything but the secret!"라고 외치는 것과 같습니다!

TypeScript에서의 Readonly 타입

Readonly 타입은 프로퍼티에 보호 마법을 씌워, 우연한 수정을 방지합니다. 모든 프로퍼티를 읽기 전용으로 만듭니다.

interface Wand {
wood: string;
core: string;
length: number;
}

const harryWand: Readonly<Wand> = {
wood: "Holly",
core: "Phoenix feather",
length: 11
};

// 이东西는 오류를 발생시킨다:
// harryWand.length = 12;

Readonly를 사용하면, 완드가 생성된 후 프로퍼티가 변경되지 않도록 보장할 수 있습니다. 마치 객체에 불가사의한 마법을 걸는 것과 같습니다!

TypeScript에서의 ReturnType 타입

ReturnType 유틸리티 타입은 함수의 반환 타입을 추출합니다. 마치 함수의 반환值을 들여다보는 Legilimency 마법을 사용하는 것과 같습니다!

function castSpell(spellName: string): { name: string, power: number } {
// 마법을 부르는 로직 여기에
return { name: spellName, power: Math.random() * 100 };
}

type SpellResult = ReturnType<typeof castSpell>;

const lumos: SpellResult = {
name: "Lumos",
power: 50
};

이 예제에서, SpellResultcastSpell의 반환 타입인 { name: string, power: number }로 추론됩니다. 복잡한 함수에서 매우 유용합니다!

TypeScript에서의 Record 타입

Record 타입은 특정 키 타입과 값 타입을 가진 객체 타입을 만드는 강력한 마법입니다. 마치 마법의 지도를 만들어 키와 값의 타입을 정의하는 것과 같습니다.

type HouseCup = Record<string, number>;

const housePoints: HouseCup = {
"Gryffindor": 472,
"Hufflepuff": 352,
"Ravenclaw": 426,
"Slytherin": 472
};

여기서, HouseCup은 키가 문자열(하우스 이름)이고 값이 숫자(점수)인 타입입니다. 객체의 올바른 구조를 보장합니다.

TypeScript에서의 NonNullable 타입

NonNullable 타입은 null과 undefined 값을 제거하여 새로운 타입을 만듭니다. 마치 null과 undefined 값을 사라지게 하는 마법을 사용하는 것과 같습니다.

type MagicalItem = string | number | null | undefined;

type DefiniteMagicalItem = NonNullable<MagicalItem>;

const definiteItem: DefiniteMagicalItem = "Invisibility Cloak";
// 이东西는 오류를 발생시킨다:
// const nullItem: DefiniteMagicalItem = null;

이 예제에서, DefiniteMagicalItem은 문자열이나 숫자 타입으로, null이나 undefined는 아닙니다. 실제 값으로 작업하고 싶을 때 완벽합니다!

유틸리티 타입 캐시트

이제 우리가 다루었던 모든 유틸리티 타입에 대한 빠른 참조 표를 제공합니다:

유틸리티 타입 설명 예제
Partial T의 모든 프로퍼티를 선택 사항으로 만들음 Partial<Wizard>
Required T의 모든 프로퍼티를 필수로 만들음 Required<MagicalCreature>
Pick<T, K> T에서 K 프로퍼티만 포함하는 새로운 타입 생성 Pick<Potion, 'name' | 'effect'>
Omit<T, K> T에서 K 프로퍼티를 제외한 새로운 타입 생성 Omit<SpellBook, 'secretSpell'>
Readonly T의 모든 프로퍼티를 읽기 전용으로 만들음 Readonly<Wand>
ReturnType T 함수의 반환 타입 추출 ReturnType<typeof castSpell>
Record<K, T> 키 타입 K와 값 타입 T를 가진 객체 타입 생성 Record<string, number>
NonNullable T에서 null과 undefined를 제외한 새로운 타입 생성 NonNullable<MagicalItem>

그렇습니다, 젊은 마법사 여러분! 이제 TypeScript 유틸리티 타입의 기본 마법을 습득했습니다. 마법은 연습을 통해 더 강력해집니다. 그러므로 계속 실험하고, 곧 "Wingardium Leviosa"와 같이 자유자재로 이 타입들을 사용할 수 있을 것입니다!

Credits: Image by storyset