Objects and Type Annotations

Objects are a fundamental part of JavaScript and TypeScript. TypeScript allows you to add type annotations to object properties, ensuring type safety when working with objects.

Inline object types

Object types can be defined inline when declaring variables:

let pizza: { name: string; price: number } = {
  name: "Margherita",
  price: 10.99
};

This tells TypeScript that pizza is an object with:

  • A name property of type string
  • A price property of type number

Type safety with objects

TypeScript ensures you use the correct types. Run the code below to see errors when trying to assign incorrect value types:

let pizza: { name: string; price: number } = {
  name: "Margherita",
  price: 10.99
};

pizza.name = "Pepperoni"; // works
pizza.price = 12.99; // works
pizza.name = 42; // error
pizza.toppings = ["pepperoni"]; // error

Loading code...

let pizza: { name: string; price: number } = {
  name: "Margherita",
  price: 10.99
};

pizza.name = "Pepperoni"; // works
pizza.price = 12.99; // works
pizza.name = 42; // error
pizza.toppings = ["pepperoni"]; // error

Optional properties

Object properties can be optional by using the ? operator:

let pizza: { name: string; price: number; toppings?: string[] } = {
  name: "Margherita",
  price: 10.99
  // toppings are optional, so we don't need to include it
};

// but can still add it later if needed
pizza.toppings = ["cheese", "basil"];
console.log(pizza);

Loading code...

let pizza: { name: string; price: number; toppings?: string[] } = {
  name: "Margherita",
  price: 10.99
  // toppings are optional, so we don't need to include it
};

// but can still add it later if needed
pizza.toppings = ["cheese", "basil"];
console.log(pizza);

Readonly properties

You can make properties read-only using the readonly keyword:

let pizza: { readonly name: string; price: number } = {
  name: "Margherita",
  price: 10.99
};

pizza.price = 12.99; // works
pizza.name = "Pepperoni"; // error: Cannot assign to 'name' because it is a read-only property
console.log(pizza.name); // works- can log/read pizza name

Loading code...

let pizza: { readonly name: string; price: number } = {
  name: "Margherita",
  price: 10.99
};

pizza.price = 12.99; // works
pizza.name = "Pepperoni"; // error: Cannot assign to 'name' because it is a read-only property
console.log(pizza.name); // works- can log/read pizza name

Nested objects

Nested objects can also have types too. This example includes a nested customer object:

let order: {
  pizzaName: string;
  customer: {
    name: string;
    address: string;
    phone: string;
  };
} = {
  pizzaName: "Margherita",
  customer: {
    name: "Homer",
    address: "123 Main St",
    phone: "555-1234"
  }
};
order.customer.name = 33; // error: Type 'number' is not assignable to type 'string'
order.customer.name = "Marge"; // works (string)
console.log(order);

Loading code...

let order: {
  pizzaName: string;
  customer: {
    name: string;
    address: string;
    phone: string;
  };
} = {
  pizzaName: "Margherita",
  customer: {
    name: "Homer",
    address: "123 Main St",
    phone: "555-1234"
  }
};
order.customer.name = 33; // error: Type 'number' is not assignable to type 'string'
order.customer.name = "Marge"; // works (string)
console.log(order);

Object methods

Methods on objects can also be typed:

let calculator: {
  add: (a: number, b: number) => number;
  subtract: (a: number, b: number) => number;
} = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b
};

Type inference with objects

TypeScript can infer object types from the initial values provided:

let person = {
  name: "Bart",
  age: 10
};
console.log(typeof(person.name));
console.log(typeof(person.age));

Loading code...

let person = {
  name: "Bart",
  age: 10
};
console.log(typeof(person.name));
console.log(typeof(person.age));

However, TypeScript will infer the exact shape, so you can't add new properties:

let person = {
  name: "bart",
  age: 10
};

person.email = "[email protected]"; // Error: Property 'email' does not exist

Loading code...

let person = {
  name: "bart",
  age: 10
};

person.email = "[email protected]"; // Error: Property 'email' does not exist

Index signatures

Index signatures can be used for objects with dynamic properties. You may not know all the properties in advance, but know the data type. This example expects a string value for the key and value:

let dictionary: { [key: string]: string } = {
  hello: "bonjour",
  goodbye: "au revoir"
};

dictionary["thank you"] = "merci"; // OK
dictionary[42] = "number"; // Works at runtime (42 is coerced to "42")

// TypeScript will catch assigning wrong value types:
dictionary["hello"] = 123; // Error: Type 'number' is not assignable to type 'string'

Loading code...

let dictionary: { [key: string]: string } = {
  hello: "bonjour",
  goodbye: "au revoir"
};

dictionary["thank you"] = "merci"; // OK
dictionary[42] = "number"; // Works at runtime (42 is coerced to "42")

// TypeScript will catch assigning wrong value types:
dictionary["hello"] = 123; // Error: Type 'number' is not assignable to type 'string'

Practical examples

Order object

let order: {
  pizzaName: string;
  quantity: number;
  total: number;
  extras?: string[];
} = {
  pizzaName: "Pepperoni",
  quantity: 2,
  total: 25.98,
  extras: ["peppers", "mushrooms"]
};

console.log(order);

Loading code...

let order: {
  pizzaName: string;
  quantity: number;
  total: number;
  extras?: string[];
} = {
  pizzaName: "Pepperoni",
  quantity: 2,
  total: 25.98,
  extras: ["peppers", "mushrooms"]
};

console.log(order);

Functions with object parameters

function createPizza(pizza: { name: string; price: number }): string {
  return `Created pizza: ${pizza.name} for $${pizza.price}`;
}

let correctPizza = createPizza({name: "Veg", price: 15.95 }); // works- types are correct
let incorrectPizza = createPizza({name: "Veg", price: "15.95" }); // error: Type 'string' is not assignable to type 'number'

Loading code...

function createPizza(pizza: { name: string; price: number }): string {
  return `Created pizza: ${pizza.name} for $${pizza.price}`;
}

let correctPizza = createPizza({name: "Veg", price: 15.95 }); // works- types are correct
let incorrectPizza = createPizza({name: "Veg", price: "15.95" }); // error: Type 'string' is not assignable to type 'number'

Objects with type annotations provide structure and type safety to your data. Coming next, we cover how to add type annotations to functions in more detail.