TypeScript: Interfaces & Types
Describe the shape of objects with interfaces and type aliases — optional fields, unions, and why structural typing is so flexible.
Beyond primitives, you'll want to describe the shape of objects. TypeScript offers interface and type for exactly that — a contract the compiler enforces everywhere the object is used.
interface User {
name: string;
age: number;
admin?: boolean; // optional (the ? )
}
function greet(u: User): string {
return `Hi ${u.name} (${u.age})`;
}
console.log(greet({ name: "Ada", age: 36 }));
// greet({ name: "Bea" }) // ❌ missing 'age'The ? marks an optional property. Pass an object missing a required field and TS errors. Note structural typing: any object with the right shape is a User — you don't have to explicitly declare that it implements the interface.
#Type aliases & unions
type ID = string | number; // union: either type is allowed
type Status = "todo" | "doing" | "done"; // literal union
function setStatus(s: Status) {
console.log("status:", s);
}
setStatus("done"); // ✅
// setStatus("nope") // ❌ not one of the allowed literals
let userId: ID = 42;
console.log(userId);interface vs type
Both describe shapes. Rule of thumb: use interface for object shapes you might extend or implement; use type for unions, primitives, and combinations (A | B, A & B). In practice they overlap a lot — pick one and stay consistent.
What does the `?` in `admin?: boolean` mean?
Key takeaways
- `interface` and `type` describe the shape of objects.
- `?` marks an optional property.
- TypeScript is structurally typed: any object with the right shape matches.
- Union types (`string | number`, or literal unions like `"todo" | "done"`) allow a value to be one of several options.
This object is rejected by TypeScript. Why?
interface User { name: string; age: number; }
const u: User = { name: 'Ada' };Which values are allowed for `s: Status`?
type Status = 'todo' | 'doing' | 'done';
let s: Status;Mark `nickname` as an optional property.
interface Person { name: string; nickname: string; }
Define an interface Product with name: string, price: number, and an optional onSale: boolean. Then write a function label(p: Product) that returns the name.
Try it live — edit the code and hit Run to see the output: