TypeScript Interfaces

Job-ready Online Courses: Knowledge Awaits – Click to Access!

One of the key features of TypeScript is its support for interfaces, which provide a way to define the shape of objects in your code. In this article, we’ll take a deep dive into TypeScript interfaces and how they can be used to improve the quality of your code.

What is an interface in TypeScript?

In TypeScript, an interface is a way to describe the shape of an object. It defines a contract that an object must adhere to, specifying the names and types of its properties and methods. Interfaces provide a level of abstraction and ensure that code adheres to a specific set of rules.

For example, let’s say we have an object representing a car. We can define an interface for the car object that specifies its properties and their types:

interface DataFlair_Car {
 make: string;
 model: string;
 year: number;
 color: string;
}

In this interface, we have defined a car object’s make, model, year, and color properties and their respective types. Any object that adheres to this interface must have these properties and their corresponding types. This allows us to write more robust code, any object we receive that is of type DataFlair_Car will have these properties.

Using interfaces in TypeScript

We can use interfaces in TypeScript in a variety of ways. One common use case is to define function parameters and return types. Let’s say we have a function that takes a car object and returns its make and model as a string:

function DataFlair_getMakeAndModel(car: Car): string {
 return `${car.make} ${car.model}`;
}

Output

Toyota Camry

In this example, we use the Car interface as the type for the car parameter. This ensures that any object passed to this function must adhere to the Car interface, including the make and model properties we access in the function body.

Another use case for interfaces is to define classes. When we define a class that implements an interface, we say that the class must have all the properties and methods defined in the interface. For example, let’s define a class that implements the DataFlair_Car interface:

class Toyota implements DataFlair_Car {
 make = 'Toyota';
 model: string;
 year: number;
 color: string;


 constructor(model: string, year: number, color: string) {
   this.model = model;
   this.year = year;
   this.color = color;
 }
}

In this example, we have defined a Toyota class that implements the DataFlair_Car interface. The class has a make property set to ‘Toyota,’ as well as the model, year, and color properties required by the interface. We also have a constructor method that takes the required parameters and assigns them to the class properties.

Optional properties

Sometimes, we may want to define an interface that has optional properties. To do this, we can use the (?) operator to mark a property as optional. For example, let’s say we have an interface for a DataFlair_Person object:

interface DataFlair_Person {
 name: string;
 age: number;
 email?: string;
}

In this interface, we have defined the name and age properties as required, but the email property is optional. Any object that adheres to this interface must have a name and age property but may or may not have an email property.

When we use an interface with optional properties, we need to check if the property exists before we try to access it. We can do this using the in-the operator or the optional chaining operator ?.. For example:

function DataFlair_sendEmail(person: Person) {
 if (person.email) {
   console.log(`Sending email to ${person.name} at ${person.email}`);
 } else {
   console.log(`No email address found for ${person.name}`);
 }
}

Output

Sending email to John at [email protected]

In this example, we check if the email property exists on the person object before using it. If it does exist, we log a message with the person’s name and email address. If it doesn’t exist, we log a message saying no email address was found.

Read-only properties

Sometimes, we may want to define an interface with read-only properties. This means that the properties can only be set when the object is created, and cannot be changed afterward. We can do this by using the read-only keyword before the property name. For example:

interface DataFlair_Point {
 readonly x: number;
 readonly y: number;
}

In this interface, we have defined two read-only properties, x, and y. This means that once a DataFlair_Point object is created, the values of these properties cannot be changed.

Extending interfaces in TypeScript

We can also extend interfaces in TypeScript to create new interfaces that inherit properties and methods from existing interfaces. This can be useful when we have multiple interfaces that share some common properties or methods. For example, let’s say we have an interface for a car:

interface DataFlair_Car {
 make: string;
 model: string;
 year: number;
 color: string;
}

We can create a new interface ElectricCar that extends the DataFlair_Car interface and adds an electricRange property:

interface ElectricCar extends DataFlair_Car {
 electricRange: number;
}

In this example, the ElectricCar interface inherits all of the properties from the DataFlair_Car interface (make, model, year, and color) and adds a new property electricRange.

Use of Interface, Interface Inheritance, Interface vs inheritance

In TypeScript, interfaces are a way of defining the shape of an object, including its properties and methods. They are often used to describe the types of function parameters and return values and to enforce type safety in your code.

Here are some key points about the use of interfaces in TypeScript:

1. Implementing an interface in TypeScript:

To implement an interface, a class must include all of the properties and methods defined in the interface. This can be done using the implements keyword. For example:

class Student implements DataFlair_Person {
 name = 'John';
 age = 21;
 sayHello() {
   console.log(`Hello, my name is ${this.name}`);
 }
}

2. Interface inheritance in TypeScript:

Interfaces can inherit from other interfaces using the extends keyword. This allows you to build more complex interfaces from simpler ones. For example:

interface DataFlairAnimal {
 name: string;
 makeSound: () => void;
}


interface Dog extends Animal {
 breed: string;
}

3. TypeScript Interface vs inheritance:

While interfaces can describe objects’ shapes, inheritance is a way of reusing code and building more complex objects from simpler ones. In TypeScript, you can use interfaces and inheritance to achieve similar goals. Still, interfaces are often preferred for defining contracts between different parts of your code, while inheritance is more appropriate for building class hierarchies and sharing implementation details.

Excess Property Checks

In TypeScript, excess property checks are a type-checking feature that helps ensure type safety when working with objects. They prevent errors that can occur when an object has additional properties that are not defined in its type.

When TypeScript checks the type of an object, it performs an excess property check to ensure that the object doesn’t have any extra properties that are not defined in its type. This check can prevent errors when an object has extra properties that are not expected.

For example, consider the following interface:

interface DataFlair_Person {
 name: string;
 age: number;
}

If we create an object that conforms to this interface, TypeScript will ensure that it has the correct properties:

const person: DataFlair_Person = {
 name: 'John',
 age: 21,
};

However, if we try to add an extra property to the object, TypeScript will throw an error:

const person: DataFlair_Person = {
 name: 'John',
 age: 21,
 gender: 'male', // Error: Object literal may only specify known properties
};

This error occurs because the DataFlair_Person interface doesn’t include a gender property, and TypeScript is performing an excess property check to ensure that the object doesn’t have any extra properties.

To work around this error, you can use a type assertion to tell TypeScript that the object has additional properties that are not defined in its type:

const person: DataFlair_Person = {
 name: 'John',
 age: 21,
 gender: 'male',
} as DataFlair_Person;

While type assertions can be useful in some cases, they can also lead to type safety issues if used improperly. Therefore, avoid using type assertions and instead ensure that your objects conform to the expected type.

Indexable Types

In TypeScript, an indexable type is an interface that describes objects that can be accessed with an index, like an array or an object. It allows you to define a type with a property that can be any type if the property name is a string or a number.

To define an indexable type, you can use the following syntax:

interface DataFlair_IndexableType {
 [key: string]: any;
}

This defines an interface with any number of properties with string keys and any value type. You can also use a number key instead of a string key to define an indexable array-like object.

You can use an indexable type to enforce type safety when working with objects with dynamic properties. For example, consider the following interface:

interface DataFlair_Book {
 title: string;
 author: string;
 [key: string]: any;
}

This interface defines a DataFlair_Book type with a title and author property and any additional properties with string keys.

You can then create an object that conforms to this type and has additional properties:

const book: DataFlair_Book = {
 title: 'The Lord of the Rings',
 author: 'J.R.R. Tolkien',
 pages: 1178,
 language: 'English',
};

TypeScript will not throw an error in this example because the DataFlair_Book interface includes an index signature that allows additional properties.

You can also use an indexable type to define more specific types of indexable objects. For example, if you want to define a type for an object with string keys and number values, you can use the following interface:

interface DataFlair_NumberDictionary {
 [key: string]: number;
}

This defines a type with any number of properties with string keys and values that must be numbers.

In summary, indexable types in TypeScript allow you to define interfaces for objects with dynamic properties while enforcing type safety. They are useful for working with objects with additional properties unknown at compile-time.

Conclusion

In this article, we’ve explored TypeScript interfaces and how they can be used to define the shape of objects in our code. We’ve seen how interfaces can ensure that our code adheres to a specific set of rules and how they can be used to create more maintainable and scalable code.

By using interfaces, we can write more self-documenting code, as it makes it clear what properties and methods an object should have. Interfaces also help to catch errors at compile time rather than runtime, saving us a lot of time and effort in debugging.

Did you like our efforts? If Yes, please give DataFlair 5 Stars on Google

courses

DataFlair Team

DataFlair Team provides high-impact content on programming, Java, Python, C++, DSA, AI, ML, data Science, Android, Flutter, MERN, Web Development, and technology. We make complex concepts easy to grasp, helping learners of all levels succeed in their tech careers.

Leave a Reply

Your email address will not be published. Required fields are marked *