react-router icon indicating copy to clipboard operation
react-router copied to clipboard

[Bug]: Typescript 5.x error when using `exactOptionalPropertyTypes`

Open gdebunne opened this issue 2 years ago • 1 comments

What version of React Router are you using?

6.10.0

Steps to Reproduce

Start from the <RouteProvider> test case above.

Edit package.json and set typescript version to 5.0.4.

Edit tsconfig.json :

  • add "exactOptionalPropertyTypes": true
  • set skipLibCheck to false
  • remove the deprecated importsNotUsedAsValues option

Reload project to make sure it is updated.

Open a new terminal and run npm run build to run tsc

Expected Behavior

react-router ts files compile with no error

Actual Behavior

node_modules/react-router/dist/lib/components.d.ts:54:30 - error TS2344: Type 'NonIndexRouteObject' does not satisfy the constraint 'AgnosticRouteObject'.

54     lazy?: LazyRouteFunction<NonIndexRouteObject>;
                                ~~~~~~~~~~~~~~~~~~~

node_modules/react-router/dist/lib/components.d.ts:73:30 - error TS2344: Type 'IndexRouteObject' does not satisfy the constraint 'AgnosticRouteObject'.

73     lazy?: LazyRouteFunction<IndexRouteObject>;
                                ~~~~~~~~~~~~~~~~

node_modules/react-router/dist/lib/context.d.ts:19:30 - error TS2344: Type 'IndexRouteObject' does not satisfy the constraint 'AgnosticRouteObject'.
  Type 'IndexRouteObject' is not assignable to type 'AgnosticIndexRouteObject' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
    Type 'IndexRouteObject' is not assignable to type 'AgnosticBaseRouteObject' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
      Types of property 'caseSensitive' are incompatible.
        Type 'boolean | undefined' is not assignable to type 'boolean'.

Details on the (recommended) exactOptionalPropertyTypes option are available here

In AgnosticBaseRouteObject, caseSensitive is declared as an optional boolean property : caseSensitive?: boolean;

In IndexRouteObject, its declaration is caseSensitive?: AgnosticIndexRouteObject["caseSensitive"]; which translates to caseSensitive?: boolean | undefined;

When using this TS option, a property with a possibly undefined value is not the same as a possibly undefined property. The two types are hence considered incompatible.

A possible fix is to declare caseSensitive?: NotUndefined<AgnosticNonIndexRouteObject["caseSensitive"]>; where type NotUndefined<T> = T extends undefined ? never : T

gdebunne avatar Apr 24 '23 20:04 gdebunne

I also run into this. I have the same flag enabled and I got this code. TypeScript want's me to either pass search as a string OR not pass it at all, but in my case, search has the type string | undefined and so it complains.

let search: string | undefined = ...;
navigate({
  pathname: `/${result.data.id}`,
  search,
})

I suggest changing the type of To to something like:

type PartialWithExplicitUndefined<T> = {
    [P in keyof T]?: T[P] | undefined;
};

type To = string | PartialWithExplicitUndefined<Path>;

hornta avatar Mar 15 '24 15:03 hornta