TypeScript course menu
TypeScript Object Types: Properties, Optional and readonly Properties, Excess Property Checks
Learn to type objects in TypeScript: object type annotations, optional properties with ?, readonly properties, nested objects, methods in object types, excess property checking for object literals, and structural typing.
On this page
Most data in JavaScript is objects: users, products, orders, settings. TypeScript describes an object's shape: which properties it has, and what type each one is.
Object type annotations#
const user: { name: string; age: number; email: string } = {
name: "Asha",
age: 21,
email: "asha@example.com",
};
console.log(`${user.name} <${user.email}>`);Asha <asha@example.com>Properties are separated by semicolons (commas also work). Writing the shape inline every time is repetitive, so in practice you give it a name with a type alias or interface:
type User = {
name: string;
age: number;
email: string;
};
const ravi: User = { name: "Ravi", age: 31, email: "ravi@example.com" };
console.log(ravi.age + 1);32The examples below use type declarations.
Missing and wrong properties#
type User = { name: string; age: number };
const meera: User = { name: "Meera" };error TS2741: Property 'age' is missing in type '{ name: string; }' but required in type 'User'.type User = { name: string; age: number };
const kiran: User = { name: "Kiran", age: "thirty" };error TS2322: Type 'string' is not assignable to type 'number'.Optional properties#
? marks a property that may be absent. Its type then includes undefined, so you must handle that case before using it:
type Product = {
name: string;
price: number;
discountPercent?: number;
};
function finalPrice(p: Product): number {
const discount = p.discountPercent ?? 0;
return p.price - (p.price * discount) / 100;
}
console.log(finalPrice({ name: "Pen", price: 20 }));
console.log(finalPrice({ name: "Bag", price: 1500, discountPercent: 10 }));20
1350type Product = { name: string; price: number; discountPercent?: number };
const bag: Product = { name: "Bag", price: 1500 };
console.log(bag.discountPercent.toFixed(1));error TS18048: 'bag.discountPercent' is possibly 'undefined'.readonly properties#
readonly stops a property being reassigned after the object is created:
type Order = {
readonly id: number;
status: string;
};
const order: Order = { id: 1042, status: "placed" };
order.status = "shipped";
order.id = 9999;error TS2540: Cannot assign to 'id' because it is a read-only property.readonly is only checked at compile time; it does not freeze the object at runtime (that is Object.freeze).
Nested objects and methods#
type Customer = {
name: string;
address: {
city: string;
pin: string;
};
greet(): string;
};
const c: Customer = {
name: "Asha",
address: { city: "Pune", pin: "411001" },
greet() {
return `Hi ${this.name} from ${this.address.city}`;
},
};
console.log(c.greet());Hi Asha from PuneMethods are written as name(params): returnType or as a property with a function type: greet: () => string.
Excess property checks#
When you write an object literal directly where a type is expected, TypeScript reports unknown properties, which catches typos:
type Settings = { theme: string; fontSize?: number };
const s: Settings = { theme: "dark", fontsize: 18 };error TS2561: Object literal may only specify known properties, but 'fontsize' does not exist in type 'Settings'. Did you mean to write 'fontSize'?Structural typing#
TypeScript compares objects by shape, not by name. Any object with the required properties fits, even if it has extra ones, as long as it is not a fresh literal (where the excess check above applies):
type Named = { name: string };
function hello(thing: Named): string {
return `Hello, ${thing.name}`;
}
const student = { name: "Ravi", course: "TypeScript", year: 2 };
console.log(hello(student)); // extra properties are fine hereHello, RaviThis is called structural typing (or "duck typing": if it has a name, it can be greeted). It is different from languages like Java, where types must be declared to implement each other.
Practice#
Practice exercise
Define a type Book with a readonly isbn string, a title, an optional subtitle, a price, and an authors array of strings. Write bookLabel(book: Book): string that returns "Title: Subtitle by A, B", leaving out the subtitle part when there is none.
Show solution
type Book = {
readonly isbn: string;
title: string;
subtitle?: string;
price: number;
authors: string[];
};
function bookLabel(book: Book): string {
const fullTitle = book.subtitle ? `${book.title}: ${book.subtitle}` : book.title;
return `${fullTitle} by ${book.authors.join(", ")}`;
}
console.log(bookLabel({ isbn: "111", title: "Learning TS", price: 499, authors: ["Asha"] }));
console.log(bookLabel({ isbn: "222", title: "Web Basics", subtitle: "HTML and CSS", price: 299, authors: ["Ravi", "Meera"] }));Learning TS by Asha
Web Basics: HTML and CSS by Ravi, MeeraKey takeaway#
Finished this lesson?