To declare a property of an interface as optional in TypeScript, we need to add ?  after the property name. For example, if a property name is age  and we want it to be optional then we need to write it as age?.

interface Animal {
  name: string;
  age?: number;
}

let dog1: Animal = {
  name: "Bruno",
};

let dog2: Animal = {
  name: "Rocky",
  age: 1,
};

console.log(dog1); // {name: 'Bruno'}
console.log(dog2); // {name: 'Rocky', age: 1}

Similarly, if you want to use an optional property in a class that implements an interface, you can write the code as:

interface Animal {
  name: string;
  age?: number;
}
class Dog implements Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

let dog = new Dog("Bruno");

console.log(dog); // Dog {name: 'Bruno'}

In this example, the class Dog does not want to have the age property in its structure hence we can completely omit it. TypeScript will not throw an error.

Similarly, if you want a class to make use of the optional property, you can write something like the below code:

interface Animal {
  name: string;
  age?: number;
}

class Cat implements Animal {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

let cat = new Cat("Mini", 1);
console.log(cat); // Cat {name: 'Mini', age: 1}

Here, unlike class Dog, class Cat makes use of the property age.

Note that, property age is not an optional property for class Cat and it must be implemented.

let cat = new Cat("Mini"); //  An argument for 'age' was not provided.

TypeScript will throw an error, An argument for 'age' was not provided.

In this case, if you want age to be optional, you can mark age property as optional in class Cat.

interface Animal {
  name: string;
  age?: number;
}

class Cat implements Animal {
  name: string;
  age?: number;

  constructor(name: string, age: number = 1) {
    this.name = name;
    this.age = age;
  }
}

let cat = new Cat("Mini");
console.log(cat); // Cat {name: 'Mini', age: 1}

If age is not passed, the constructor will assign a default value of 1.

Similarly, if you do not want to assign a default value, you can make the constructor parameter use the optional parameter age?.

interface Animal {
  name: string;
  age?: number;
}

class Cat implements Animal {
  name: string;
  age?: number;

  constructor(name: string, age?: number) {
    this.name = name;
    if (age) this.age = age;
  }
}

let cat = new Cat("Mini");
console.log(cat); // Cat {name: 'Mini'}

let cat2 = new Cat("Mono", 3);
console.log(cat2); // Cat {name: 'Mono', age: 3}

Optional methods

Just like field properties, we can also declare optional methods in an interface.

 interface Animal {
  speak?(): void;
}

class Cat implements Animal {}

class Dog implements Animal {
  speak() {
    console.log("Woof Woof");
  }
}

let dog = new Dog();

dog.speak(); // Woof Woof

In the above example, speak() method is defined as optional property in Animal interface using the ? symbol. The class Cat does not implement it. But another class Dog implements it and TypeScript will run without any error.