TypeScript enhances object types and interfaces with static typing, extensibility, and powerful type-system features. These features help make code more structured, maintainable, and type-safe.
1. Interfaces Can Describe Functions
Interfaces in TypeScript are not limited to objects. They can also define the signature of a function, specifying its parameters and return type.
interface GreetFunction {
(name: string): string;
}
const greet: GreetFunction = (name) => `Hello, ${name}!`;
console.log(greet("Alen"));
Here, GreetFunction requires a function that accepts a string and returns a string.
2. Objects Can Have Optional Properties
You can mark object properties as optional using ?. An optional property does not have to be provided when creating an object.
interface User {
name: string;
age?: number;
}
const user: User = {
name: "Alen",
};
Here, age is optional, while name is required.
3. Interfaces Support Declaration Merging
TypeScript can automatically merge multiple declarations with the same interface name. This feature is called declaration merging.
interface Person {
name: string;
}
interface Person {
age: number;
}
const person: Person = {
name: "Alen",
age: 25,
};
After merging, Person contains both name and age.
4. Interfaces Can Extend Other Interfaces
An interface can extend one or more interfaces to reuse and combine their properties.
interface Person {
name: string;
}
interface Employee extends Person {
employeeId: number;
}
const employee: Employee = {
name: "Alen",
employeeId: 123,
};
Here, Employee inherits the name property from Person and adds employeeId.
5. Interfaces Can Extend Classes
An interface can extend a class. In this case, the interface includes the class's accessible instance members in its type.
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
interface Employee extends Person {
employeeId: number;
}
const employee: Employee = {
name: "Alen",
employeeId: 123,
};
The object satisfies the structural type requirements of Employee. The interface does not inherit the class's implementation or constructor behavior.
6. Objects Can Use unknown for Flexible Properties
The unknown type allows a property to contain a value of any type while requiring type checking before most operations are performed on it.
interface User {
id: number;
metadata: unknown;
}
const user: User = {
id: 1,
metadata: { role: "admin" },
};
if (
typeof user.metadata === "object" &&
user.metadata !== null &&
"role" in user.metadata
) {
console.log(user.metadata.role); // admin
}
Here, metadata can contain different types of values, but TypeScript requires appropriate checks before accessing its properties.
7. Interfaces Can Extend Array Types
Interfaces can extend existing array types to add or restrict specific properties or indexes.
interface Point2D extends Array<number> {
0: number;
1: number;
}
const coords: Point2D = [10, 20];
Here, Point2D is an array of numbers with required elements at indexes 0 and 1.
For simply representing a two-dimensional point, however, a tuple is usually clearer:
type Point2D = [number, number];
const coords: Point2D = [10, 20];
8. Utility Types Can Modify Object Structures
TypeScript provides built-in utility types such as Partial, Required, Pick, and Omit for creating modified versions of existing object types.
interface User {
id: number;
name: string;
age: number;
}
type UserPreview = Pick<User, "id" | "name">;
const user: UserPreview = {
id: 1,
name: "Alen",
};
Here, Pick creates a new type containing only the selected properties from User.
9. Objects Can Use Intersection Types
Intersection types combine multiple types into a single type. The resulting type must satisfy all the combined types.
type Person = {
name: string;
};
type Employee = {
employeeId: number;
};
const employee: Person & Employee = {
name: "Alen",
employeeId: 123,
};