Type Aliases — Naming your Shapes
If you type the same shape twice, it's time for a type alias.
In the last chapter, you saw how inline object types can get massive and downright unreadable. Imagine writing something like { name: string; age: number; email: string; address: { city: string; state: string; pin: number } } every single time you need to describe a user. That is painful, error-prone, and makes your code a nightmare to maintain. If you change the shape later, you have to hunt down every single place you wrote it inline. There has to be a better way — and there is.
Type Aliases solve this problem completely. They let you name a type shape once and reuse it everywhere. Think of it like your "regular order" at your favourite Irani cafe. Instead of saying "Bhai, ek chicken dum biryani, double masala, extra raita, mirchi ka salan, aur double ka meetha" every single time you visit, you just say "Bhai, regular order!" The staff knows EXACTLY what you mean because you have defined it once and they remember it. That is exactly what a type alias does for your code.
You define the shape once, give it a memorable name, and then just use the name everywhere. Here is a quick preview:
type BiryaniOrder = {
type: string;
spice: "mild" | "medium" | "hot";
extraRaita: boolean;
};
Now, instead of repeating that entire object shape every time, you simply say BiryaniOrder. Clean, readable, and maintainable. The compiler replaces the name with the full shape behind the scenes — so there is zero performance cost. You get readability without any downside. This is the power of type aliases, and by the end of this chapter, you will be using them everywhere.
The syntax for creating a type alias is straightforward. You use the type keyword, followed by the name you want to give (in PascalCase), an equals sign, and then the type definition. Here is the basic form:
type User = {
name: string;
age: number;
email: string;
};
Now you can use User anywhere instead of the full inline type. Watch how it simplifies everything:
function greet(user: User): string {
return `Hello, ${user.name}!`;
}
function getUsers(): User[] {
return [{ name: "Imran", age: 25, email: "imran@hyd.dev" }];
}
const currentUser: User = {
name: "Imran",
age: 25,
email: "imran@hyd.dev"
};
Every one of those usages would have required the full inline { name: string; age: number; email: string } shape without the alias. With it, the code reads naturally and any change to the User shape only needs to happen in one place.
Type aliases are not just for objects though — you can alias any type in TypeScript:
type ID = string; // primitive alias
type Points = number[]; // array alias
type Callback = (data: string) => void; // function alias
type Status = "active" | "inactive" | "banned"; // union alias
You can also nest type aliases, combining them to build complex shapes from simpler ones:
type Address = {
city: string;
pin: number;
};
type User = {
name: string;
address: Address; // using another type alias!
};
This composability is where type aliases really shine. You build small, focused types and combine them into larger ones — just like building blocks. And here is the critical thing to remember: type aliases are exactly equivalent to writing the inline type. There is zero runtime difference. TypeScript erases all types during compilation, so your JavaScript output is identical whether you used a type alias or wrote the inline type a hundred times. Type aliases exist purely for developer experience and readability. They are a compile-time convenience with no runtime cost.
Type aliases are not just simple renames — they can combine and compose types in powerful ways that form the backbone of advanced TypeScript patterns. Let us explore the key advanced patterns you will encounter and use regularly.
1. Union Aliases: You can define a type that is one of several options. This is incredibly useful for modelling results, states, or categories:
type Success = { data: string };
type Error = { message: string };
type Result = Success | Error;
const response: Result = { data: "Biryani ready!" };
// or: const response: Result = { message: "Out of stock" };
Union aliases let you express "this OR that" cleanly. We will dive deeper into unions in Stage 3.
2. Intersection Aliases: You can combine multiple types into one using the & operator. This is like extending an existing type with extra fields:
type User = { name: string; email: string };
type Employee = User & {
employeeId: string;
department: string;
};
const dev: Employee = {
name: "Farhan",
email: "farhan@hyd.dev",
employeeId: "HYD-042",
department: "Engineering"
};
Intersections are perfect for building on top of existing types without repeating fields.
3. Generic Type Aliases: You can create type aliases that accept type parameters, making them reusable with any data type:
type ApiResponse<T> = {
data: T;
status: number;
message: string;
};
type UserResponse = ApiResponse<User>;
type ProductResponse = ApiResponse<Product>;
Generics are covered in detail in Stage 5, but this preview shows you how type aliases integrate with them seamlessly.
4. Recursive Types: A type alias can reference itself, which is essential for tree structures, linked lists, and nested data:
type TreeNode = {
value: number;
left?: TreeNode;
right?: TreeNode;
};
The optional ? is crucial here — without it, TypeScript would require infinite nesting. The optional property provides the "escape hatch" that makes the recursion terminate.
5. Mapped Types Preview: You can transform existing types using mapped types — a preview of the powerful type-level programming coming in Stage 7:
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
This takes every key in User and makes it readonly. It is a taste of the metaprogramming capabilities TypeScript offers.
Naming Conventions: Always use PascalCase for type names (User, ApiResponse, BiryaniOrder). Choose descriptive, specific names. Avoid generic names like Data, Thing, or MyType — they tell you nothing about what the shape represents. A good type alias name should make the code self-documenting.
Type aliases are simple in concept, but there are several traps that catch developers — especially those transitioning from JavaScript or coming fresh to TypeScript. Let us walk through the most common ones so you can avoid them.
Trap 1: Type aliases are NOT objects at runtime. This is the number one confusion. When you write type User = { name: string }, TypeScript creates zero JavaScript code. You cannot do User.name, Object.keys(User), or instanceof User. The type alias is a compile-time only construct — it exists solely for the TypeScript compiler to check your code. Once compilation is done, it vanishes completely. Think of it like the menu description at an Irani cafe — it tells you what the dish contains, but you cannot eat the menu itself!
type User = { name: string };
// User.name ❌ Error — 'User' only refers to a type
// Object.keys(User) ❌ Error — 'User' is not a value
// instanceof User ❌ Error — right-hand side not a class
Trap 2: Circular references without indirection cause errors. TypeScript handles circular references in object types lazily, so type A = { b: B }; type B = { a: A } works fine — the object structure provides indirection. But a direct circular alias like type A = B; type B = A; is an immediate error because there is no object structure to break the cycle. The compiler cannot resolve what A actually is — it just points to B, which points back to A, forever.
// ✅ Works — object structure breaks the cycle
type A = { b: B };
type B = { a: A };
// ❌ Error — circular alias with no structure
type X = Y;
type Y = X; // Error: Type alias 'X' circularly references itself
Trap 3: Type aliases vs const — completely different things! A type and a const may sometimes look similar, but they are fundamentally different. type Color = "red" | "blue" is erased at runtime — no JavaScript code is generated. const Color = { red: "red", blue: "blue" } is a runtime JavaScript object that actually exists in memory. Do not confuse them! One is for the type system, the other is for runtime values.
type Color = "red" | "blue"; // Compile-time only, ERASED
const Colors = { red: "red", blue: "blue" } as const; // Runtime value
// Colors exists at runtime — you can log it
console.log(Colors.red); // "red"
// Color does NOT exist at runtime — you cannot log it
Trap 4: Cannot reassign a type alias in the same scope. Unlike variables, you cannot redeclare a type alias. type X = string; type X = number; is a hard error — TypeScript will not allow duplicate type alias definitions in the same scope. If you need a different shape, create a new type alias with a different name.
Trap 5: Type alias naming conflicts. If you name a type alias the same as a variable or class, it can confuse readers of your code. For example, having both class User {} and type User = {} in the same file is technically allowed (TypeScript uses declaration merging in some cases), but it is bad practice. Use distinct names like type UserType or type UserData if you also have a User class.
Here is your complete cheatsheet for type aliases — everything you need at a glance. Bookmark this, screenshot it, stick it on your monitor next to your Irani chai.
Basic Type Alias
type User = {
name: string;
age: number;
};
Primitive Alias
type ID = string;
Array Alias
type Names = string[];
Function Alias
type Fn = (a: number) => string;
Union Alias
type Status = "on" | "off";
Intersection Alias
type Full = TypeA & TypeB;
Nested Aliases
type Inner = { city: string };
type Outer = { inner: Inner };
Generic Alias
type Box<T> = { value: T };
Key Rules
- Always use PascalCase —
User, notuserorUSER - Type aliases are compile-time only — they produce zero JavaScript output
- They are just names for shapes — zero runtime cost, pure developer convenience
- Use them whenever you would repeat an inline type more than once — DRY principle applied to types
- They can reference other type aliases — compose small types into bigger ones
- Cannot redeclare in the same scope —
type X = string; type X = number;is an error - Not the same as const — types are erased, const values exist at runtime
The golden rule: "If you type the same shape twice, it is time for a type alias. Name your shapes — your future self will thank you!" Every time you find yourself copy-pasting an inline object type, stop and create a type alias instead. Your codebase will be cleaner, your team will thank you, and you will catch more bugs because a single type definition is easier to get right than five scattered inline types.
Key Takeaways — Type Aliases
- Type aliases let you NAME a type shape once and reuse it everywhere — DRY for types
- Use the `type` keyword: `type User = { name: string; age: number }`
- Aliases work for any type — objects, primitives, arrays, functions, unions
- Nest and compose aliases: `type User = { name: string; address: Address }`
- Type aliases are compile-time only — zero runtime JavaScript output
- Use PascalCase naming, avoid generic names like `Data` or `Thing`
- Cannot redeclare a type alias in the same scope
- Types ≠ const — types are erased, const values exist at runtime
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login