Type Inference
TypeScript can often guess types automatically. This is called type inference. When you assign a value without an explicit type, TypeScript infers it. For example:
let count = 10; // inferred as number
let message = "Hello"; // inferred as string
The compiler knows count
is a number and message
is a string from these initial values. Type inference helps keep code concise and still type-safe. You don't always need to write : number
or : string
because TypeScript figures it out.
Why Use Type Inference
Less code: You skip repetitive type annotations.
Safety: Even with fewer annotations, TypeScript checks types.
Readability: Code stays clean while avoiding obvious errors.
However, you can still add type annotations if it makes the code clearer or if a variable’s type isn’t obvious. For instance, when you declare an empty array:
let items = []; // inferred as any[]
Here, you might want to specify the type explicitly:
let items: number[] = []; // explicitly an array of numbers
Type Alias
A type alias lets you give a name to any type. It is like a custom shorthand for a type definition. For example, if you want a type for IDs that could be either numbers or strings, you could write:
type ID = number | string;
let userId: ID = "user123"; // valid
This makes code easier to read and reuse. You can also make a type alias for an object shape:
type User = { name: string; age: number };
let newUser: User = { name: "Alice", age: 30 };
When to Use Type Alias
Union or tuple types: Like the
ID
example above or arrays with mixed types.Complex type definitions: If a type would be hard to read inline, alias it.
Reusability: Use aliases for types you’ll reuse often.
Immutability: A type alias cannot be changed after declaration (it cannot be reopened or extended like an interface).
Interface
An interface in TypeScript is a way to define the shape of an object. It is written using the interface
keyword. For example:
interface Person {
name: string;
age: number;
}
let person: Person = { name: "Bob", age: 25 };
Interfaces can also extend other interfaces. This is useful for building on existing object shapes:
interface Employee extends Person {
role: string;
}
let employee: Employee = { name: "Carol", age: 28, role: "Developer" };
When to Use Interfaces
Object shapes: Best for defining objects and class contracts.
Extensibility: You can extend or merge interfaces.
Classes: Classes can implement interfaces to ensure they have certain properties.
Consistency: Some teams prefer interfaces for defining data models and API shapes.
Interfaces and type aliases often overlap. Generally, use interfaces when defining object structures (especially for classes) and type aliases for unions or complex types.
Union vs Intersection Types
Union types (|
) allow a variable to be one of several types. For example, you might have:
type Status = "success" | "error" | "loading";
let state: Status = "success"; // OK
This means state
can be any of the listed strings.
Intersection types (&
) combine multiple types into one. For example:
type A = { x: number };
type B = { y: string };
type Point = A & B; // Point has both x and y
let point: Point = { x: 10, y: "hello" };
Use union types when a value could be one type or another, and intersection types when a value must satisfy multiple type requirements (combining properties of both).
Generics
Generics allow you to write flexible, reusable code that works with different types. A generic introduces a placeholder type (often T
) that is filled in when the function or class is used. For example, a function that returns whatever you pass in:
function identity<T>(value: T): T {
return value;
}
let num = identity(5); // T is number
let str = identity("Hi"); // T is string
Here, identity
works with any type: number, string, object, etc. The type T
is determined from the argument.
You can also use generics with classes or other structures. For example, a class that holds a value:
class Box<T> {
constructor(public content: T) {}
}
let numberBox = new Box(123);
let stringBox = new Box("hello");
The Box
class works with any type, thanks to <T>
.
Why Use Generics
Reusability: Write code without specifying exact types upfront.
Type safety: Generics keep track of types, so you still get errors if used incorrectly.
Flexibility: Useful for data structures (like arrays, maps) and functions that should work with multiple types.
Summary
Type Inference: TypeScript often figures out types automatically from assigned values, making code shorter and safer.
Type Alias: Use
type
to name a type (primitives, unions, tuples, etc.). Great for unions or any reusable type.Interface: Use
interface
to define object shapes or class contracts. Interfaces can extend each other and can be implemented by classes.Union vs Intersection: A union (
|
) type means one of several types. An intersection (&
) type means combining multiple types together.Generics: Allow writing functions or classes with flexible types. Use
<T>
to make code reusable and type-safe.
Each feature helps you write clearer code. As a beginner, know that type aliases and interfaces often overlap; use whichever feels clear for your case. Start with interfaces for object shapes and use type aliases for unions or complex types. Generics and inference help keep your code flexible and reduce repetition.
Understanding these concepts will help you write robust TypeScript code from the start. Good luck and happy coding!
Top comments (2)
Insightful article
Thank You.