DEV Community

Nhan Nguyen
Nhan Nguyen

Posted on

Beginner's TypeScript #22

Image description

Error Message: "Types Of Property Are Incompatible"

We have a routingConfig **object which contains some routes. These routes are an array of objects, each with a **path and a component property, both of which are expected to be strings:

const routingConfig = {
  routes: [
    {
      path: "home",
      component: "HomeComponent",
    },
    {
      path: "about",
      component: "AboutComponent",
    },
    {
      path: "contact",
      component: "ContactComponent",
    },
  ],
}
Enter fullscreen mode Exit fullscreen mode

We also have a createRoutes function that takes this routingConfig, and maps it into a type:

const createRoutes = (config: {
    routes: {
        path: string;
        component: string;
    }[];
}) => {};
Enter fullscreen mode Exit fullscreen mode

When calling createRoutes with the routingConfig, we get a large error message:

createRoutes(routingConfig); // red squiggly line under routingConfig

// hovering over routingConfig shows:
Argument of type '{ routes: { path: string; component: string | number; }[]; }' is not assignable to parameter of type '{ routes: { path: string; component: string; }[]; }'.
  Types of property 'routes' are incompatible.
    Type '{ path: string; component: string | number; }[]' is not assignable to type '{ path: string; component: string; }[]'.
      Type '{ path: string; component: string | number; }' is not assignable to type '{ path: string; component: string; }'.
        Types of property 'component' are incompatible.
          Type 'string | number' is not assignable to type 'string'.
            Type 'number' is not assignable to type 'string'.
Enter fullscreen mode Exit fullscreen mode

We will figure out what this error is telling us, and make the error more readable.

We have a large error message that was difficult to understand:

Argument of type '{ routes: { path: string; component: string | number; }[]; }' is not assignable to parameter of type '{ routes: { path: string; component: string; }[]; }'.

Types of property 'routes' are incompatible.

Type '{ path: string; component: string | number; }[]' is not assignable to type '{ path: string; component: string; }[]'.

Type '{ path: string; component: string | number; }' is not assignable to type '{ path: string; component: string; }'.

Types of property 'component' are incompatible.

Type 'string | number' is not assignable to type 'string'.

Type 'number' is not assignable to type 'string'.
Enter fullscreen mode Exit fullscreen mode

The first line tells us that when we pass the routingConfig object into the createRoutes function, the shape of the object is not compatible with the shape of the parameter.

Next, we see that the routes are incompatible.

Continuing on, we can see that a union of string | number is trying to be assigned to just string.

Finally, we see that number is not assignable to string.

Usually, when faced with a large error message like this, it is a good idea to start from the bottom.

Since we have a number being passed into a slot that expects a string, we can scan upwards to identify the property that is causing the issue. In this case, it is the component property.

Inside of the routes array, the about path's component property is 12 instead of a string:

const routingConfig = {
    routes: [
        {
            path: "home",
            component: "HomeComponent",
        },
        {
            path: "about",
            component: 12, // Error occurs here
        },
        {
            path: "contact",
            component: "ContactComponent",
        },
    ],
}
Enter fullscreen mode Exit fullscreen mode

Now, we know what is causing the error and even have some ideas about how to fix it, but we still need to clean up the error message.

👉 Solution

There are a few ways to make the error readable and informative:

1️⃣ Pass the Object Inline

One way to make the error message better is instead of declaring routingConfig as a separate object, pass it inline:

createRoutes({
    routes: [
        {
            path: "home",
            component: "HomeComponent",
        },
        {
            path: "about",
            component: 12, // Error occurs here
        },
        {
            path: "contact",
            component: "ContactComponent",
        },
    ],
})
Enter fullscreen mode Exit fullscreen mode

This lets TypeScript make better assumptions about the object, resulting in a more relevant error message.

Hovering over **createRoutes **shows us the shape:

const createRoutes: (config: {
    routes: {
        path: string;
        component: string;
    }[];
}) => void;
Enter fullscreen mode Exit fullscreen mode

Now there will be an error underlining the component where the error occurs:

...
{
    path: "about",
    component: 12,  // red squiggly line under component
},
...
Enter fullscreen mode Exit fullscreen mode

Hovering over component to see the error message, it is more clear:

Type 'number' is not assignable to type 'string'.

The expected type comes from property 'component' which is declared here on type '{ path: string; component: string; }'
Enter fullscreen mode Exit fullscreen mode

Having the separate routingConfig object does not give us nice error messages because TypeScript is not sure if we will be using it in a different scenario elsewhere in the code.

2️⃣ Use Type Annotations

Assigning explicit type annotations to the objects also will help TypeScript deliver a more concrete error message.

First, create a new RoutingConfig type, then use it as the config type for the createRoutes function:

type RoutingConfig = {
    routes: {
        path: string;
        component: string;
    }[];
};

const createRoutes = (config: RoutingConfig) => {};
Enter fullscreen mode Exit fullscreen mode

Then we can also annotate the routingConfig object:

const routingConfig: RoutingConfig = {
    routes: [
        {
            path: "home",
            component: "HomeComponent",
        },
        {
            path: "about",
            component: "AboutComponent",
        },
        {
            path: "contact",
            component: "ContactComponent",
        },
    ],
}
Enter fullscreen mode Exit fullscreen mode

Again, this will give us a more relevant error message inside of routingConfig.

3️⃣ Use the satisfies Keyword

Finally, we could use the satifies keyword on the routingConfig object to specify it as a RoutingConfig type:

const routingConfig: RoutingConfig = {
    routes: [
        {
            path: "home",
            component: "HomeComponent",
        },
        {
            path: "about",
            component: 12, // red squiggly line under component
        },
        {
            path: "contact",
            component: "ContactComponent",
        },
    ],
} satisfies RoutingConfig;
Enter fullscreen mode Exit fullscreen mode

All of these strategies are great for getting TypeScript to provide more useful error messages.


I hope you found it useful. Thanks for reading. 🙏

Let's get connected! You can find me on:

Top comments (0)