Almost all real data is objects; interfaces are how a codebase names and shares their shapes.
An object type lists properties and their types: { id: number; name: string }. Separators can be ; or ,. When a shape is used in more than one place, give it a name with an interface:
interface User {
id: number;
name: string;
email?: string; // optional
greet(other: User): string; // method
}Any object with at least those properties satisfies User — no implements or class needed, because typing is structural. Extra properties are fine when you pass an existing variable.
There is one exception you will hit quickly: an object literal written directly where a type is expected gets an excess property check. const u: User = { id: 1, name: "A", emial: "x" } is an error, because a property the type doesn't know about is most likely a typo.
Interfaces can extend others, combining shapes:
interface Admin extends User { permissions: string[] }Accessing a property that isn't declared is an error, which catches typos. When an object is used as a dictionary with arbitrary keys, use an index signature — { [key: string]: number } — or the Record<string, number> utility type.
Optional properties are typed T | undefined when you read them, so you must check before using: user.email?.toLowerCase().
Interfaces and object types also describe functions' parameters and return values, nested objects, and arrays of objects, so you will write them constantly. Name the shapes that cross module boundaries; inline the one-off ones.
Example
interface Address {
city: string;
zip?: string; // optional
}
interface User {
id: number;
name: string;
address: Address;
greet(other: User): string;
}
interface Admin extends User {
permissions: string[];
}
const ada: Admin = {
id: 1, name: "Ada", address: { city: "London" }, permissions: ["all"],
greet(other) { return `Hi ${other.name}, I'm ${this.name}`; },
};
console.log(ada.address.zip?.trim() ?? "no zip", ada.greet(ada));
// @ts-expect-error -- Object literal may only specify known properties
const typo: Address = { city: "Paris", zpi: "75001" };Remember
- Interfaces name object shapes; any object with those members fits
prop?: Tmarks optional properties (read asT | undefined)- Object literals get excess property checks that catch typos
extendsbuilds bigger interfaces from smaller ones
Watch out for
- Assuming excess property checks always apply — they only apply to fresh object literals.