diff --git a/.changeset/tiny-gifts-wash.md b/.changeset/tiny-gifts-wash.md new file mode 100644 index 0000000000..7b0a67767e --- /dev/null +++ b/.changeset/tiny-gifts-wash.md @@ -0,0 +1,5 @@ +--- +"react-router": patch +--- + +Update `ServerBuild` type to ensure compatibility with TypeScript's `exactOptionalPropertyTypes` option diff --git a/contributors.yml b/contributors.yml index dd0d8c165f..4cb0e8c96a 100644 --- a/contributors.yml +++ b/contributors.yml @@ -303,6 +303,7 @@ - SkayuX - skratchdot - smithki +- smorimoto - soartec-lab - sorokya - sorrycc diff --git a/integration/typegen-test.ts b/integration/typegen-test.ts index 1460c528ab..1115315c93 100644 --- a/integration/typegen-test.ts +++ b/integration/typegen-test.ts @@ -490,6 +490,30 @@ test.describe("typegen", () => { expect(proc.status).toBe(0); }); + test("works with tsconfig 'exactOptionalPropertyTypes' set to 'true'", async () => { + const cwd = await createProject({ + "vite.config.ts": viteConfig, + "app/routes.ts": tsx` + import { type RouteConfig } from "@react-router/dev/routes"; + export default [] satisfies RouteConfig; + `, + "app/handler.ts": tsx` + import { createRequestHandler } from "react-router"; + import * as serverBuild from "virtual:react-router/server-build"; + export default createRequestHandler(serverBuild); + `, + }); + + const tsconfig = await fse.readJson(path.join(cwd, "tsconfig.json")); + tsconfig.compilerOptions.exactOptionalPropertyTypes = true; + await fse.writeJson(path.join(cwd, "tsconfig.json"), tsconfig); + + const proc = typecheck(cwd); + expect(proc.stdout.toString()).toBe(""); + expect(proc.stderr.toString()).toBe(""); + expect(proc.status).toBe(0); + }); + test("dynamic import matches 'createRequestHandler' function argument type", async () => { const cwd = await createProject({ "vite.config.ts": viteConfig, diff --git a/packages/react-router-dev/typegen/index.ts b/packages/react-router-dev/typegen/index.ts index 342f27f808..5db028c324 100644 --- a/packages/react-router-dev/typegen/index.ts +++ b/packages/react-router-dev/typegen/index.ts @@ -152,7 +152,7 @@ function register(ctx: Context) { const virtual = ts` declare module "virtual:react-router/server-build" { - import { ServerBuild } from "react-router"; + import type { ServerBuild } from "react-router"; import { ServerBuild } from "react-router"; export const assets: ServerBuild["assets"]; export const assetsBuildDirectory: ServerBuild["assetsBuildDirectory"]; export const basename: ServerBuild["basename"]; diff --git a/packages/react-router/lib/server-runtime/build.ts b/packages/react-router/lib/server-runtime/build.ts index 1fd78abdb7..9502fd4150 100644 --- a/packages/react-router/lib/server-runtime/build.ts +++ b/packages/react-router/lib/server-runtime/build.ts @@ -24,14 +24,20 @@ export interface ServerBuild { }; routes: ServerRouteManifest; assets: AssetsManifest; - basename?: string; + // `| undefined` is required to ensure compatibility with TypeScript's + // `exactOptionalPropertyTypes` option + basename?: string | undefined; publicPath: string; assetsBuildDirectory: string; future: FutureConfig; ssr: boolean; - unstable_getCriticalCss?: (args: { - pathname: string; - }) => OptionalCriticalCss | Promise; + unstable_getCriticalCss?: + | ((args: { + pathname: string; + }) => OptionalCriticalCss | Promise) + // `| undefined` is required to ensure compatibility with TypeScript's + // `exactOptionalPropertyTypes` option + | undefined; /** * @deprecated This is now done via a custom header during prerendering */