TypeScript: How to Create a New Type from an Old Type

Updated: January 7, 2024 By: Guest Contributor Post a comment

Overview

TypeScript’s alchemy allows for the transmutation of types, transforming old types into gleaming new ones. This guide will meander through the art of forging fresh types from existent material, a pursuit sure to indulge the whims of any code craftsman.

Creating Type Aliases

Firstly, let us speak of type aliases, fellow artisans. In TypeScript’s dominion, a type alias is much like a pseudonym, a new name for an existing type. ‘Tis a simple sorcery:

type Fruit = string;
const apple: Fruit = 'Granny Smith'; // Right as rain

We can also refine and recondition old types, customizing as one might tailor a coat:

type Point = { x: number; y: number; };
type 3DPoint = Point & { z: number; };
const point3D: 3DPoint = { x: 1, y: 2, z: 3 }; // Summit of elegance

Pickin’ and Choosin’ with Utility Types

By the capers of TypeScript’s utility types, a blacksmith could chisel away the excess or shield up with more substance:

type Creature = { name: string; age: number; habitat: string; };
type NameAndHabitat = Pick<Creature, 'name' | 'habitat'>;
const robin: NameAndHabitat = { name: 'Robin', habitat: 'Woods' }; // Ain't got no age, 'tis unburdened

There are times when we fancy obscuring particular properties, so as not to give away the complete memoir of our object:

type MysteriousCreature = Omit<Creature, 'age'>;
const mysterious: MysteriousCreature = { name: 'Drake', habitat: 'Unknown' }; // Shrouded in mystery, it is

Morphin’ Unions into Intersection Types

Moving on, imagine one wishes to conjoin properties from multiple cuttings into a singular, more all-encompassing garment. This is managed through intersection types:

type Employee = { id: number; name: string; };
type Manager = { id: number; privileges: string[]; };
type Leader = Employee & Manager;
const boss: Leader = { id: 1, name: 'Napoleon', privileges: ['Leadership'] }; // As commandin' as a general

A Polymorphic Approach with Generic Types

In the realm of TypeScript, a generic type is akin to a mold that can be poured into manifold forms. To bind a new type from a pliable old type, engage the arcane of generics:

type Container<T> = { value: T };
const stringContainer: Container<string> = { value: 'Rumors' }; // Whispers of a string
const numberContainer: Container<number> = { value: 42 }; // Murmurs of a number

‘Tis a powerful enchantment, allowing for repeated use of the type with different substances:

function wrapInArray<T>(value: T): T[] {
  return [value];
}

const stringArray = wrapInArray('Inheritance'); // 'Tis an array with legacy

MUTating Types with Conditional Types

Conditional types let one type answer the question, ‘What form might you take under yon conditions?’:

type Check<T> = T extends string ? 'It's a string' : 'It's not a string';
type IsString = Check<string>; // Responds, 'It's a string'
type IsNotString = Check<number>; // Replies, 'It's not a string'

‘Tis akin to a fork in the trail, where the path taken depends on the traveler’s nature.

Advanced Shape-shifting with Mapped Types

We traverse further to the hinterland of advanced type manipulation – the mapped types. These types allow one to traverse an object’s keys and cobble a type anew:

type Permissions = { read: boolean; write: boolean; execute: boolean; };
type OptionalPermissions = { [K in keyof Permissions]?: Permissions[K] };
const guestPermissions: OptionalPermissions = { read: true }; // A guest, with but a glance at possibilities

Such is the versatility of TypeScript, ’tis like having a shapeless hat that can assume the fit of any head!

Conclusion

In closing, TypeScript furnishes a carpenter’s workshop brimming with tools, without whose craft, our code would seem a mere pile of splinters. Hedging their bets with TypeScript’s myriad ways to create new types from old, engineers gain the prowess to wield types like swords, crafting and honing until each is a testimonial to its purpose.