Interface Extensions/Merging in TypeScript
Tags: typescript, javascript, beginners, programming
Published: 2023-03-28 06:13:21

TypeScript has the capability of extending and merging interfaces.

Our development will be more efficient by utilizing these advanced features.

## Basic Usage of Interfaces

Interfaces are similar to object-type aliases. Here is a comparison between an interface and an aliased object type.

```tsx
type Vehicle = {
    wheels: number;
    maker: string;
};

interface Vehicle {
    wheels: number;
    maker: string;
}
```

This type and interface can be used in almost the same way.

In this article, I will dig deeper into the interface to introduce more advanced techniques.

## Interface Extensions

You may notice that some interfaces sometimes look similar to each other when it comes to a big project. If some interfaces have common properties, it is a good time to extend one interface and make another one. Let me show an example:

```tsx
interface Vehicle {
    wheels: number;
    maker?: string;
}

interface Car extends Vehicle {
    power: "gas" | "electricity";
}

interface Bicycle extends Vehicle {
    folding: boolean;
}

const myCar: Car = {
    wheels: 4,
    maker: "Toyota",
    power: "gas"
} // OK

const car: Car = {
    wheels: 4,
    maker: "Honda",
    power: "gas",
    folding: false
} // Error
// Type '{ wheels: number; maker: string; power: "gas"; folding: boolean; }' is not assignable to type 'Car'.
    // Object literal may only specify known properties, and 'folding' does not exist in type 'Car'.

const bicycle: Bicycle = {
    wheels: 2,
} // Error
// Property 'folding' is missing in type '{ wheels: number; }' but required in type 'Bicycle'.
```

Both Car and Bicycle interfaces have common properties, wheels (required) and a maker (optional). Therefore, they can derive these properties from the Vehicle interface.

Cool! You can avoid repetitive code by using this technique.

### What If You Declare the Same Property in a Child Interface?

It is possible to declare the same name of the property again when making a descendant interface. However, it does not mean you can freely override the parent’s property. The rule is you can override a property as long as a new re-declared property is assignable to the parent one.

Here is an example:

```tsx
interface Vehicle {
    wheels: number;
    maker?: string;
}

interface Car extends Vehicle {
    power: "gas" | "electricity";
}

interface ElectricCar extends Car {
    power: "electricity";
} // OK

interface FuturisticCar extends Car {
    power: "air";
} // Error
// Interface 'FuturisticCar' incorrectly extends interface 'Car'.
  // Types of property 'power' are incompatible.
    // Type '"air"' is not assignable to type '"gas" | "electricity"'.
```

As you can see above, `"electricity"` is assignable to the literal union type, `"gas" | "electricity"`, because `"electricity"` is included in the union type. Thus, it’s possible to make `ElectricCar` by extending the `Car` interface.

On the other hand, the TypeScript type checker yells at you when making the `FuturisticCar` interface because `"air"` is not included in `"gas" | "electricity”`.

## Interface Merging

In the previous section, I explained what happens when you declare the same property while extending another interface.

Next, guess what happens when you declare multiple interfaces with the same name.

```tsx
interface Vehicle {
    wheels: number;
    maker?: string;
}

interface Vehicle {
    price: number;
}

const vehicle: Vehicle = {
    wheels: 4,
    maker: "Nissan",
    price: 25000
} // OK

const vehicle2: Vehicle = {
    maker: "Nissan",
    price: 25000
} // Error
// Property 'wheels' is missing in type '{ maker: string; price: number; }' but required in type 'Vehicle'.

const vehicle3: Vehicle = {
    wheels: 4,
    maker: "Nissan",
} // Error
// Property 'price' is missing in type '{ wheels: number; maker: string; }' but required in type 'Vehicle'.
```

In this example, `Vehicle` is declared twice. Since both interfaces have the same name, TypeScript merges them into one interface. As a result, the final `Vehicle` interface has `wheels`, `maker`, and `price` properties. Technically, it is the same as declaring one `Vehicle` interface with the three properties.

But what if those interfaces have the same property name with different types?

Let’s take a look at the example code below:

```tsx
interface Vehicle {
    wheels: number;
    maker?: string;
    plate: number;
}

interface Vehicle {
    plate: string;
} // Error
// Subsequent property declarations must have the same type.  Property 'plate' must be of type 'number', but here has type 'string'.
```

It is not allowed to declare the same property with different types because it causes conflicts. The TypeScript type checker will warn that subsequent property declarations must have the same type.

## Conclusion

- Interface extensions allow developers to avoid repetitive code by deriving common properties from a parent interface.
- When declaring the same property in a child interface, it is only allowed if the new re-declared property is assignable to the parent one.
- Interface merging can be used to combine interfaces with the same name, but it is not allowed to declare the same property with different types as it causes conflicts.