diff --git a/packages/react-router/src/index.tsx b/packages/react-router/src/index.tsx index dc15af0bb9..34ee024a57 100644 --- a/packages/react-router/src/index.tsx +++ b/packages/react-router/src/index.tsx @@ -329,6 +329,7 @@ export type { Expand, MergeAll, Assign, + IntersectAssign, } from './utils' export type { diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts index 9c5c265296..2c90f27698 100644 --- a/packages/react-router/src/route.ts +++ b/packages/react-router/src/route.ts @@ -31,6 +31,7 @@ import type { Constrain, ConstrainLiteral, Expand, + IntersectAssign, NoInfer, } from './utils' import type { BuildLocationFn, NavigateFn } from './RouterProvider' @@ -676,7 +677,7 @@ export type ResolveFullSearchSchema< TSearchValidator, > = unknown extends TParentRoute ? ResolveValidatorOutput - : Assign< + : IntersectAssign< InferFullSearchSchema, ResolveValidatorOutput > @@ -684,7 +685,7 @@ export type ResolveFullSearchSchema< export type ResolveFullSearchSchemaInput< TParentRoute extends AnyRoute, TSearchValidator, -> = Assign< +> = IntersectAssign< InferFullSearchSchemaInput, ResolveSearchValidatorInput > diff --git a/packages/react-router/src/utils.ts b/packages/react-router/src/utils.ts index a58be8a8fe..188428fa8b 100644 --- a/packages/react-router/src/utils.ts +++ b/packages/react-router/src/utils.ts @@ -54,11 +54,17 @@ export type IsUnion = ( ? false : true +export type IsNonEmptyObject = T extends object + ? keyof T extends never + ? false + : true + : false + export type Assign = TLeft extends any ? TRight extends any - ? keyof TLeft extends never + ? IsNonEmptyObject extends false ? TRight - : keyof TRight extends never + : IsNonEmptyObject extends false ? TLeft : keyof TLeft & keyof TRight extends never ? TLeft & TRight @@ -66,6 +72,16 @@ export type Assign = TLeft extends any : never : never +export type IntersectAssign = TLeft extends any + ? TRight extends any + ? IsNonEmptyObject extends false + ? TRight + : IsNonEmptyObject extends false + ? TLeft + : TRight & TLeft + : never + : never + export type Timeout = ReturnType export type Updater = diff --git a/packages/react-router/tests/link.test-d.tsx b/packages/react-router/tests/link.test-d.tsx index e11cb0314a..7c58c25837 100644 --- a/packages/react-router/tests/link.test-d.tsx +++ b/packages/react-router/tests/link.test-d.tsx @@ -51,12 +51,13 @@ const invoicesIndexRoute = createRoute({ const invoiceRoute = createRoute({ getParentRoute: () => invoicesRoute, path: '$invoiceId', - validateSearch: () => ({ page: 0 }), + validateSearch: (): { page?: number } => ({ page: 0 }), }) const invoiceEditRoute = createRoute({ getParentRoute: () => invoiceRoute, path: 'edit', + validateSearch: () => ({ editId: 0 }), }) const invoiceDetailsRoute = createRoute({ @@ -82,13 +83,33 @@ const linesRoute = createRoute({ }, }) +const linesFormRoute = createRoute({ + getParentRoute: () => linesRoute, + path: 'form', + validateSearch: (): { mode: 'new' | 'edit' | 'view' } => ({ mode: 'view' }), +}) + +const linesFormEditRoute = createRoute({ + getParentRoute: () => linesFormRoute, + path: 'edit', + validateSearch: (): { mode: 'view' | 'edit' | 'cancel' } => ({ + mode: 'view', + }), +}) + const routeTreeTuples = rootRoute.addChildren([ postsRoute.addChildren([postRoute, postsIndexRoute]), invoicesRoute.addChildren([ invoicesIndexRoute, invoiceRoute.addChildren([ invoiceEditRoute, - invoiceDetailsRoute.addChildren([detailRoute.addChildren([linesRoute])]), + invoiceDetailsRoute.addChildren([ + detailRoute.addChildren([ + linesRoute.addChildren([ + linesFormRoute.addChildren([linesFormEditRoute]), + ]), + ]), + ]), ]), ]), indexRoute, @@ -101,7 +122,11 @@ const routeTreeObjects = rootRoute.addChildren({ invoiceRoute: invoiceRoute.addChildren({ invoiceEditRoute, invoiceDetailsRoute: invoiceDetailsRoute.addChildren({ - detailRoute: detailRoute.addChildren({ linesRoute }), + detailRoute: detailRoute.addChildren({ + linesRoute: linesRoute.addChildren({ + linesFormRoute: linesFormRoute.addChildren({ linesFormEditRoute }), + }), + }), }), }), }), @@ -172,6 +197,8 @@ test('when navigating to the root', () => { | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/edit' | '/posts' | '/posts/$postId' @@ -188,6 +215,8 @@ test('when navigating to the root', () => { | '/invoices/$invoiceId' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/edit' | '/posts' @@ -205,6 +234,8 @@ test('when navigating to the root', () => { | '/invoices/$invoiceId/' | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/edit/' | '/posts/' @@ -222,6 +253,8 @@ test('when navigating to the root', () => { | '/invoices/$invoiceId' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/edit' | '/posts' @@ -245,6 +278,10 @@ test('when navigating to the root', () => { | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/edit' @@ -314,6 +351,8 @@ test('when navigating to the root', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(DefaultRouterObjectsLink) @@ -325,6 +364,8 @@ test('when navigating to the root', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(RouterAlwaysTrailingSlashLink) @@ -336,6 +377,8 @@ test('when navigating to the root', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(RouterNeverTrailingSlashLink) @@ -347,6 +390,8 @@ test('when navigating to the root', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(RouterPreserveTrailingSlashLink) @@ -358,6 +403,8 @@ test('when navigating to the root', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(DefaultRouterLink) @@ -400,6 +447,8 @@ test('when navigating from a route with no params and no search to the root', () | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/posts' | '/posts/$postId' | '$postId' @@ -419,6 +468,8 @@ test('when navigating from a route with no params and no search to the root', () | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/posts' | '/posts/$postId' | '$postId' @@ -438,6 +489,8 @@ test('when navigating from a route with no params and no search to the root', () | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/posts/' | '/posts/$postId/' | '$postId/' @@ -457,6 +510,8 @@ test('when navigating from a route with no params and no search to the root', () | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/posts' | '/posts/$postId' | '$postId' @@ -484,6 +539,10 @@ test('when navigating from a route with no params and no search to the root', () | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/posts' | '/posts/' | '/posts/$postId' @@ -728,6 +787,8 @@ test('when navigating from a route with no params and no search to the parent ro | '../invoices/$invoiceId/details' | '../invoices/$invoiceId/details/$detailId' | '../invoices/$invoiceId/details/$detailId/lines' + | '../invoices/$invoiceId/details/$detailId/lines/form' + | '../invoices/$invoiceId/details/$detailId/lines/form/edit' | '../invoices' | '..' | undefined @@ -744,6 +805,8 @@ test('when navigating from a route with no params and no search to the parent ro | '../invoices/$invoiceId/details' | '../invoices/$invoiceId/details/$detailId' | '../invoices/$invoiceId/details/$detailId/lines' + | '../invoices/$invoiceId/details/$detailId/lines/form' + | '../invoices/$invoiceId/details/$detailId/lines/form/edit' | '../invoices' | '..' | undefined @@ -760,6 +823,8 @@ test('when navigating from a route with no params and no search to the parent ro | '../invoices/$invoiceId/details/' | '../invoices/$invoiceId/details/$detailId/' | '../invoices/$invoiceId/details/$detailId/lines/' + | '../invoices/$invoiceId/details/$detailId/lines/form/' + | '../invoices/$invoiceId/details/$detailId/lines/form/edit/' | '../invoices/' | '../' | undefined @@ -776,6 +841,8 @@ test('when navigating from a route with no params and no search to the parent ro | '../invoices/$invoiceId/details' | '../invoices/$invoiceId/details/$detailId' | '../invoices/$invoiceId/details/$detailId/lines' + | '../invoices/$invoiceId/details/$detailId/lines/form' + | '../invoices/$invoiceId/details/$detailId/lines/form/edit' | '../invoices' | '..' | undefined @@ -799,6 +866,10 @@ test('when navigating from a route with no params and no search to the parent ro | '../invoices/$invoiceId/details/$detailId/' | '../invoices/$invoiceId/details/$detailId/lines' | '../invoices/$invoiceId/details/$detailId/lines/' + | '../invoices/$invoiceId/details/$detailId/lines/form' + | '../invoices/$invoiceId/details/$detailId/lines/form/edit' + | '../invoices/$invoiceId/details/$detailId/lines/form/' + | '../invoices/$invoiceId/details/$detailId/lines/form/edit/' | '../invoices' | '../invoices/' | '../' @@ -821,6 +892,8 @@ test('cannot navigate to a branch with an index', () => { | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '.' | '..' >() @@ -838,6 +911,8 @@ test('cannot navigate to a branch with an index', () => { | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '.' | '..' >() @@ -857,6 +932,8 @@ test('cannot navigate to a branch with an index', () => { | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | './' | '../' >() @@ -874,6 +951,8 @@ test('cannot navigate to a branch with an index', () => { | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '.' | '..' >() @@ -901,6 +980,10 @@ test('cannot navigate to a branch with an index', () => { | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '.' | '..' | './' @@ -927,6 +1010,8 @@ test('from autocompletes to all absolute routes', () => { | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | undefined >() @@ -945,6 +1030,8 @@ test('from autocompletes to all absolute routes', () => { | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | undefined >() }) @@ -968,6 +1055,8 @@ test('from does not allow invalid routes', () => { | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | undefined >() @@ -986,6 +1075,8 @@ test('from does not allow invalid routes', () => { | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | undefined >() }) @@ -2305,23 +2396,23 @@ test('when navigating from a route with search params to the same route', () => expectTypeOf(DefaultRouterLink) .parameter(0) - .toMatchTypeOf<{ search: unknown }>() + .not.toMatchTypeOf<{ search: unknown }>() expectTypeOf(DefaultRouterObjectsLink) .parameter(0) - .toMatchTypeOf<{ search: unknown }>() + .not.toMatchTypeOf<{ search: unknown }>() expectTypeOf(RouterAlwaysTrailingSlashesLink) .parameter(0) - .toMatchTypeOf<{ search: unknown }>() + .not.toMatchTypeOf<{ search: unknown }>() expectTypeOf(RouterNeverTrailingSlashesLink) .parameter(0) - .toMatchTypeOf<{ search: unknown }>() + .not.toMatchTypeOf<{ search: unknown }>() expectTypeOf(RouterPreserveTrailingSlashesLink) .parameter(0) - .toMatchTypeOf<{ search: unknown }>() + .not.toMatchTypeOf<{ search: unknown }>() expectTypeOf(DefaultRouterLink) .parameter(0) @@ -2331,7 +2422,7 @@ test('when navigating from a route with search params to the same route', () => expectTypeOf(DefaultRouterObjectsLink) .parameter(0) - .toMatchTypeOf<{ search: unknown }>() + .not.toMatchTypeOf<{ search: unknown }>() expectTypeOf(RouterAlwaysTrailingSlashesLink) .parameter(0) @@ -2431,47 +2522,52 @@ test('when navigating to a route with search params', () => { defaultRouterLinkSearch .exclude() - .toEqualTypeOf<{ rootPage?: number; page: number }>() + .toEqualTypeOf<{ rootPage?: number; page?: number; editId: number }>() defaultRouterObjectsLinkSearch .exclude() - .toEqualTypeOf<{ rootPage?: number; page: number }>() + .toEqualTypeOf<{ rootPage?: number; page?: number; editId: number }>() routerAlwaysTrailingSlashesLinkSearch .exclude() - .toEqualTypeOf<{ rootPage?: number; page: number }>() + .toEqualTypeOf<{ rootPage?: number; page?: number; editId: number }>() routerNeverTrailingSlashesLinkSearch .exclude() - .toEqualTypeOf<{ rootPage?: number; page: number }>() + .toEqualTypeOf<{ rootPage?: number; page?: number; editId: number }>() routerPreserveTrailingSlashesLinkSearch .exclude() - .toEqualTypeOf<{ rootPage?: number; page: number }>() + .toEqualTypeOf<{ rootPage?: number; page?: number; editId: number }>() defaultRouterLinkSearch.returns.toEqualTypeOf<{ - page: number + page?: number rootPage?: number + editId: number }>() defaultRouterObjectsLinkSearch.returns.toEqualTypeOf<{ - page: number + page?: number rootPage?: number + editId: number }>() routerAlwaysTrailingSlashesLinkSearch.returns.toEqualTypeOf<{ - page: number + page?: number rootPage?: number + editId: number }>() routerNeverTrailingSlashesLinkSearch.returns.toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() routerPreserveTrailingSlashesLinkSearch.returns.toEqualTypeOf<{ - page: number + page?: number rootPage?: number + editId: number }>() defaultRouterLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2479,6 +2575,8 @@ test('when navigating to a route with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() defaultRouterObjectsLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2486,6 +2584,8 @@ test('when navigating to a route with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerAlwaysTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2493,6 +2593,8 @@ test('when navigating to a route with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerNeverTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2500,6 +2602,8 @@ test('when navigating to a route with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerPreserveTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2507,6 +2611,8 @@ test('when navigating to a route with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() }) @@ -2664,6 +2770,8 @@ test('when navigating to a route with optional search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() defaultRouterObjectsLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2671,6 +2779,8 @@ test('when navigating to a route with optional search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerAlwaysTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2678,6 +2788,8 @@ test('when navigating to a route with optional search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerNeverTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2685,6 +2797,8 @@ test('when navigating to a route with optional search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerPreserveTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2692,6 +2806,8 @@ test('when navigating to a route with optional search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() }) @@ -2774,51 +2890,60 @@ test('when navigating from a route with no search params to a route with search defaultRouterLinkSearch.exclude().toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() defaultRouterObjectsLinkSearch.exclude().toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() routerAlwaysTrailingSlashesLinkSearch .exclude() .toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() routerNeverTrailingSlashesLinkSearch .exclude() .toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() defaultRouterLinkSearch.returns.toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() defaultRouterObjectsLinkSearch.returns.toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() routerAlwaysTrailingSlashesLinkSearch.returns.toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() routerNeverTrailingSlashesLinkSearch.returns.toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() routerPreserveTrailingSlashesLinkSearch.returns.toEqualTypeOf<{ rootPage?: number - page: number + page?: number + editId: number }>() defaultRouterLinkSearch.parameter(0).toEqualTypeOf<{ rootPage?: number }>() @@ -2923,51 +3048,61 @@ test('when navigating to a union of routes with search params', () => { defaultRouterLinkSearch .exclude() .toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } | undefined + | { rootPage?: number; page?: number; editId: number } + | { rootPage?: number } + | undefined >() defaultRouterObjectsLinkSearch .exclude() .toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } | undefined + | { rootPage?: number; page?: number; editId: number } + | { rootPage?: number } + | undefined >() routerAlwaysTrailingSlashesSearch .exclude() .toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } | undefined + | { rootPage?: number; page?: number; editId: number } + | { rootPage?: number } + | undefined >() routerNeverTrailingSlashesSearch .exclude() .toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } | undefined + | { rootPage?: number; page?: number; editId: number } + | { rootPage?: number } + | undefined >() routerPreserveTrailingSlashesSearch .exclude() .toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } | undefined + | { rootPage?: number; page?: number; editId: number } + | { rootPage?: number } + | undefined >() defaultRouterLinkSearch.returns.toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } + { rootPage?: number; page?: number; editId: number } | { rootPage?: number } >() defaultRouterObjectsLinkSearch.returns.toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } + { rootPage?: number; page?: number; editId: number } | { rootPage?: number } >() routerAlwaysTrailingSlashesSearch.returns.toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } + { rootPage?: number; page?: number; editId: number } | { rootPage?: number } >() routerNeverTrailingSlashesSearch.returns.toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } + { rootPage?: number; page?: number; editId: number } | { rootPage?: number } >() routerPreserveTrailingSlashesSearch.returns.toEqualTypeOf< - { rootPage?: number; page: number } | { rootPage?: number } + { rootPage?: number; page?: number; editId: number } | { rootPage?: number } >() defaultRouterLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2975,6 +3110,8 @@ test('when navigating to a union of routes with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() defaultRouterObjectsLinkSearch.parameter(0).toEqualTypeOf<{ @@ -2982,6 +3119,8 @@ test('when navigating to a union of routes with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerAlwaysTrailingSlashesSearch.parameter(0).toEqualTypeOf<{ @@ -2989,6 +3128,8 @@ test('when navigating to a union of routes with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerNeverTrailingSlashesSearch.parameter(0).toEqualTypeOf<{ @@ -2996,6 +3137,8 @@ test('when navigating to a union of routes with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerPreserveTrailingSlashesSearch.parameter(0).toEqualTypeOf<{ @@ -3003,6 +3146,8 @@ test('when navigating to a union of routes with search params', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() }) @@ -3091,7 +3236,7 @@ test('when navigating to a union of routes with search params including the root .exclude() .toEqualTypeOf< | { rootPage?: number } - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | undefined >() @@ -3100,7 +3245,7 @@ test('when navigating to a union of routes with search params including the root .exclude() .toEqualTypeOf< | { rootPage?: number } - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | undefined >() @@ -3109,7 +3254,7 @@ test('when navigating to a union of routes with search params including the root .exclude() .toEqualTypeOf< | { rootPage?: number } - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | undefined >() @@ -3118,7 +3263,7 @@ test('when navigating to a union of routes with search params including the root .exclude() .toEqualTypeOf< | { rootPage?: number } - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | undefined >() @@ -3127,37 +3272,37 @@ test('when navigating to a union of routes with search params including the root .exclude() .toEqualTypeOf< | { rootPage?: number } - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | undefined >() defaultRouterSearch.returns.toEqualTypeOf< - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | { rootPage?: number } >() defaultRouterObjectsSearch.returns.toEqualTypeOf< - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | { rootPage?: number } >() routerAlwaysTrailingSlashesLinkSearch.returns.toEqualTypeOf< - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | { rootPage?: number } >() routerNeverTrailingSlashesLinkSearch.returns.toEqualTypeOf< - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | { rootPage?: number } >() routerPreserveTrailingSlashesLinkSearch.returns.toEqualTypeOf< - | { rootPage?: number; page: number } + | { rootPage?: number; page?: number; editId: number } | { rootPage?: number; rootIndexPage: number } | { rootPage?: number } >() @@ -3167,6 +3312,8 @@ test('when navigating to a union of routes with search params including the root rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() defaultRouterObjectsSearch.parameter(0).toEqualTypeOf<{ @@ -3174,6 +3321,8 @@ test('when navigating to a union of routes with search params including the root rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerAlwaysTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -3181,6 +3330,8 @@ test('when navigating to a union of routes with search params including the root rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerNeverTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -3188,6 +3339,8 @@ test('when navigating to a union of routes with search params including the root rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() routerPreserveTrailingSlashesLinkSearch.parameter(0).toEqualTypeOf<{ @@ -3195,6 +3348,8 @@ test('when navigating to a union of routes with search params including the root rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() }) @@ -3576,6 +3731,8 @@ test('when passing a component with props to createLink and navigating to the ro | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/edit' | '/posts' | '/posts/$postId' @@ -3592,6 +3749,8 @@ test('when passing a component with props to createLink and navigating to the ro | '/invoices/$invoiceId' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/edit' | '/posts' @@ -3609,6 +3768,8 @@ test('when passing a component with props to createLink and navigating to the ro | '/invoices/$invoiceId/' | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/edit/' | '/posts/' @@ -3626,6 +3787,8 @@ test('when passing a component with props to createLink and navigating to the ro | '/invoices/$invoiceId' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/edit' | '/posts' @@ -3649,6 +3812,10 @@ test('when passing a component with props to createLink and navigating to the ro | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/edit' @@ -3718,6 +3885,8 @@ test('when passing a component with props to createLink and navigating to the ro rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(DefaultRouterObjectsLink) @@ -3729,6 +3898,8 @@ test('when passing a component with props to createLink and navigating to the ro rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(RouterAlwaysTrailingSlashLink) @@ -3740,6 +3911,8 @@ test('when passing a component with props to createLink and navigating to the ro rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(RouterNeverTrailingSlashLink) @@ -3751,6 +3924,8 @@ test('when passing a component with props to createLink and navigating to the ro rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(RouterPreserveTrailingSlashLink) @@ -3762,6 +3937,8 @@ test('when passing a component with props to createLink and navigating to the ro rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'new' | 'edit' | 'view' }>() expectTypeOf(DefaultRouterLink) @@ -3980,6 +4157,8 @@ test('navigation edge cases', () => { | '../../invoices/$invoiceId/details' | '../../invoices/$invoiceId/details/$detailId' | '../../invoices/$invoiceId/details/$detailId/lines' + | '../../invoices/$invoiceId/details/$detailId/lines/form' + | '../../invoices/$invoiceId/details/$detailId/lines/form/edit' | undefined >() @@ -3996,6 +4175,8 @@ test('navigation edge cases', () => { | '../../invoices/$invoiceId/details/' | '../../invoices/$invoiceId/details/$detailId/' | '../../invoices/$invoiceId/details/$detailId/lines/' + | '../../invoices/$invoiceId/details/$detailId/lines/form/' + | '../../invoices/$invoiceId/details/$detailId/lines/form/edit/' | undefined >() @@ -4012,6 +4193,8 @@ test('navigation edge cases', () => { | '../../invoices/$invoiceId/details' | '../../invoices/$invoiceId/details/$detailId' | '../../invoices/$invoiceId/details/$detailId/lines' + | '../../invoices/$invoiceId/details/$detailId/lines/form' + | '../../invoices/$invoiceId/details/$detailId/lines/form/edit' | undefined >() @@ -4031,6 +4214,8 @@ test('navigation edge cases', () => { | '../../invoices/$invoiceId/details' | '../../invoices/$invoiceId/details/$detailId' | '../../invoices/$invoiceId/details/$detailId/lines' + | '../../invoices/$invoiceId/details/$detailId/lines/form' + | '../../invoices/$invoiceId/details/$detailId/lines/form/edit' | '../../posts/' | '../../posts/$postId/' | '../../invoices/' @@ -4039,6 +4224,8 @@ test('navigation edge cases', () => { | '../../invoices/$invoiceId/details/' | '../../invoices/$invoiceId/details/$detailId/' | '../../invoices/$invoiceId/details/$detailId/lines/' + | '../../invoices/$invoiceId/details/$detailId/lines/form/' + | '../../invoices/$invoiceId/details/$detailId/lines/form/edit/' | undefined >() }) @@ -4079,6 +4266,8 @@ test('linkOptions', () => { | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/edit' | '/posts' | '/posts/$postId' @@ -4095,6 +4284,8 @@ test('linkOptions', () => { | '/invoices/$invoiceId' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/edit' | '/posts' @@ -4112,6 +4303,8 @@ test('linkOptions', () => { | '/invoices/$invoiceId/' | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/edit/' | '/posts/' @@ -4129,6 +4322,8 @@ test('linkOptions', () => { | '/invoices/$invoiceId' | '/invoices/$invoiceId/details/$detailId' | '/invoices/$invoiceId/details/$detailId/lines' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/edit' | '/posts' @@ -4152,6 +4347,10 @@ test('linkOptions', () => { | '/invoices/$invoiceId/details/$detailId/' | '/invoices/$invoiceId/details/$detailId/lines' | '/invoices/$invoiceId/details/$detailId/lines/' + | '/invoices/$invoiceId/details/$detailId/lines/form' + | '/invoices/$invoiceId/details/$detailId/lines/form/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' | '/invoices/$invoiceId/details' | '/invoices/$invoiceId/details/' | '/invoices/$invoiceId/edit' @@ -4221,6 +4420,8 @@ test('linkOptions', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'view' | 'edit' | 'new' }>() expectTypeOf(defaultRouterObjectsLinkOptions) @@ -4232,6 +4433,8 @@ test('linkOptions', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'view' | 'edit' | 'new' }>() expectTypeOf(routerAlwaysTrailingSlashLinkOptions) @@ -4243,6 +4446,8 @@ test('linkOptions', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'view' | 'edit' | 'new' }>() expectTypeOf(routerNeverTrailingSlashLinkOptions) @@ -4254,6 +4459,8 @@ test('linkOptions', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'view' | 'edit' | 'new' }>() expectTypeOf(routerPreserveTrailingSlashLinkOptions) @@ -4265,6 +4472,8 @@ test('linkOptions', () => { rootIndexPage?: number rootPage?: number linesPage?: number + editId?: number + mode?: 'edit' | 'view' | 'new' }>() expectTypeOf(defaultRouterLinkOptions) @@ -4317,3 +4526,90 @@ test('linkOptions', () => { to: '/' }>() }) + +test('when navigating to a route with conflicting validateSearch', () => { + const DefaultRouterLink = Link< + DefaultRouter, + string, + '/invoices/$invoiceId/details/$detailId/lines/form/edit' + > + + const DefaultRouterObjectsLink = Link< + DefaultRouterObjects, + string, + '/invoices/$invoiceId/details/$detailId/lines/form/edit' + > + + const RouterAlwaysTrailingSlashLink = Link< + RouterAlwaysTrailingSlashes, + string, + '/invoices/$invoiceId/details/$detailId/lines/form/edit/' + > + + const RouterNeverTrailingSlashLink = Link< + RouterNeverTrailingSlashes, + string, + '/invoices/$invoiceId/details/$detailId/lines/form/edit' + > + const RouterPreserveTrailingSlashLink = Link< + RouterPreserveTrailingSlashes, + string, + | '/invoices/$invoiceId/details/$detailId/lines/form/edit/' + | '/invoices/$invoiceId/details/$detailId/lines/form/edit' + > + + expectTypeOf(DefaultRouterLink) + .parameter(0) + .toHaveProperty('search') + .exclude() + .toEqualTypeOf<{ + mode: 'edit' | 'view' + linesPage?: number | undefined + page?: number | undefined + rootPage?: number | undefined + }>() + + expectTypeOf(DefaultRouterObjectsLink) + .parameter(0) + .toHaveProperty('search') + .exclude() + .toEqualTypeOf<{ + mode: 'edit' | 'view' + linesPage?: number | undefined + page?: number | undefined + rootPage?: number | undefined + }>() + + expectTypeOf(RouterAlwaysTrailingSlashLink) + .parameter(0) + .toHaveProperty('search') + .exclude() + .toEqualTypeOf<{ + mode: 'edit' | 'view' + linesPage?: number | undefined + page?: number | undefined + rootPage?: number | undefined + }>() + + expectTypeOf(RouterNeverTrailingSlashLink) + .parameter(0) + .toHaveProperty('search') + .exclude() + .toEqualTypeOf<{ + mode: 'edit' | 'view' + linesPage?: number | undefined + page?: number | undefined + rootPage?: number | undefined + }>() + + expectTypeOf(RouterPreserveTrailingSlashLink) + .parameter(0) + .toHaveProperty('search') + .exclude() + .toEqualTypeOf<{ + mode: 'edit' | 'view' + linesPage?: number | undefined + page?: number | undefined + rootPage?: number | undefined + }>() +}) diff --git a/packages/start-client/src/createMiddleware.ts b/packages/start-client/src/createMiddleware.ts index 566a082b2c..52dc8bf143 100644 --- a/packages/start-client/src/createMiddleware.ts +++ b/packages/start-client/src/createMiddleware.ts @@ -3,12 +3,13 @@ import type { Assign, Constrain, Expand, + IntersectAssign, ResolveValidatorInput, ResolveValidatorOutput, SerializerStringify, } from '@tanstack/react-router' -export type MergeAllMiddleware< +export type AssignAllMiddleware< TMiddlewares, TType extends keyof AnyMiddleware['_types'], TAcc = undefined, @@ -16,48 +17,67 @@ export type MergeAllMiddleware< infer TMiddleware extends AnyMiddleware, ...infer TRest, ] - ? MergeAllMiddleware> + ? AssignAllMiddleware< + TRest, + TType, + Assign + > : TAcc -export type MergeAllClientAfterContext< +export type AssignAllClientAfterContext< TMiddlewares, TClientContext = undefined, TClientAfterContext = undefined, > = unknown extends TClientContext ? TClientContext : Assign< - MergeAllMiddleware, + AssignAllMiddleware, Assign > /** * Recursively resolve the client context type produced by a sequence of middleware */ -export type MergeAllClientContext< +export type AssignAllClientContext< TMiddlewares, TContext = undefined, > = unknown extends TContext ? TContext - : Assign, TContext> + : Assign, TContext> /** * Recursively resolve the server context type produced by a sequence of middleware */ -export type MergeAllServerContext< +export type AssignAllServerContext< TMiddlewares, TContext = undefined, > = unknown extends TContext ? TContext - : Assign, TContext> + : Assign, TContext> + +export type IntersectAllMiddleware< + TMiddlewares, + TType extends keyof AnyMiddleware['_types'], + TAcc = undefined, +> = TMiddlewares extends readonly [ + infer TMiddleware extends AnyMiddleware, + ...infer TRest, +] + ? IntersectAllMiddleware< + TRest, + TType, + IntersectAssign + > + : TAcc /** * Recursively resolve the input type produced by a sequence of middleware */ -export type MergeAllValidatorInputs = +export type IntersectAllValidatorInputs = unknown extends TValidator ? TValidator - : Assign< - MergeAllMiddleware, + : IntersectAssign< + IntersectAllMiddleware, TValidator extends undefined ? undefined : ResolveValidatorInput @@ -65,11 +85,11 @@ export type MergeAllValidatorInputs = /** * Recursively merge the output type produced by a sequence of middleware */ -export type MergeAllValidatorOutputs = +export type IntersectAllValidatorOutputs = unknown extends TValidator ? TValidator - : Assign< - MergeAllMiddleware, + : IntersectAssign< + IntersectAllMiddleware, TValidator extends undefined ? undefined : ResolveValidatorOutput @@ -121,8 +141,8 @@ export interface MiddlewareServerFnOptions< in out TValidator, in out TServerContext, > { - data: Expand> - context: Expand> + data: Expand> + context: Expand> next: MiddlewareServerNextFn method: Method filename: string @@ -156,8 +176,8 @@ export interface MiddlewareClientFnOptions< in out TMiddlewares, in out TValidator, > { - data: Expand> - context: Expand> + data: Expand> + context: Expand> sendContext?: unknown // cc Chris Horobin method: Method next: MiddlewareClientNextFn @@ -192,9 +212,9 @@ export interface MiddlewareClientAfterFnOptions< in out TClientContext, in out TClientAfterContext, > { - data: Expand> + data: Expand> context: Expand< - MergeAllClientAfterContext< + AssignAllClientAfterContext< TMiddlewares, TClientContext, TClientAfterContext @@ -254,15 +274,15 @@ export interface MiddlewareTypes< _types: { middlewares: TMiddlewares input: ResolveValidatorInput - allInput: MergeAllValidatorInputs + allInput: IntersectAllValidatorInputs output: ResolveValidatorOutput - allOutput: MergeAllValidatorOutputs + allOutput: IntersectAllValidatorOutputs clientContext: TClientContext - allClientContext: MergeAllClientContext + allClientContext: AssignAllClientContext serverContext: TServerContext - allServerContext: MergeAllServerContext + allServerContext: AssignAllServerContext clientAfterContext: TClientAfterContext - allClientAfterContext: MergeAllClientAfterContext< + allClientAfterContext: AssignAllClientAfterContext< TMiddlewares, TClientContext, TClientAfterContext diff --git a/packages/start-client/src/createServerFn.ts b/packages/start-client/src/createServerFn.ts index fb3723a51b..6c03d40ce5 100644 --- a/packages/start-client/src/createServerFn.ts +++ b/packages/start-client/src/createServerFn.ts @@ -14,9 +14,9 @@ import type { } from '@tanstack/react-router' import type { AnyMiddleware, - MergeAllServerContext, - MergeAllValidatorInputs, - MergeAllValidatorOutputs, + AssignAllServerContext, + IntersectAllValidatorInputs, + IntersectAllValidatorOutputs, MiddlewareClientFnResult, MiddlewareServerFnResult, } from './createMiddleware' @@ -33,7 +33,7 @@ export type CompiledFetcherFnOptions = { } export type Fetcher = - undefined extends MergeAllValidatorInputs + undefined extends IntersectAllValidatorInputs ? OptionalFetcher : RequiredFetcher @@ -67,12 +67,12 @@ export type FetcherBaseOptions = { export interface RequiredFetcherDataOptions extends FetcherBaseOptions { - data: Expand> + data: Expand> } export interface OptionalFetcherDataOptions extends FetcherBaseOptions { - data?: Expand> + data?: Expand> } export type FetcherData = @@ -92,8 +92,8 @@ export type ServerFn = ( export interface ServerFnCtx { method: TMethod - data: Expand> - context: Expand> + data: Expand> + context: Expand> } export type CompiledFetcherFn = { diff --git a/packages/start-client/src/index.tsx b/packages/start-client/src/index.tsx index bbdb5c0dfe..93fddbf75c 100644 --- a/packages/start-client/src/index.tsx +++ b/packages/start-client/src/index.tsx @@ -23,8 +23,8 @@ export { } from './createServerFn' export { createMiddleware, - type MergeAllValidatorInputs, - type MergeAllValidatorOutputs, + type IntersectAllValidatorInputs, + type IntersectAllValidatorOutputs, type MiddlewareServerFn, type AnyMiddleware, type MiddlewareOptions, @@ -40,10 +40,10 @@ export { type MiddlewareClientNextFn, type ClientAfterResultWithContext, type ClientResultWithContext, - type MergeAllClientAfterContext, - type MergeAllClientContext, - type MergeAllMiddleware, - type MergeAllServerContext, + type AssignAllClientAfterContext, + type AssignAllClientContext, + type AssignAllMiddleware, + type AssignAllServerContext, type MiddlewareAfterValidator, type MiddlewareClientAfterNextFn, type MiddlewareClientFn, diff --git a/packages/start-client/src/tests/createServerFn.test-d.tsx b/packages/start-client/src/tests/createServerFn.test-d.tsx index b2736e359d..630838e9a1 100644 --- a/packages/start-client/src/tests/createServerFn.test-d.tsx +++ b/packages/start-client/src/tests/createServerFn.test-d.tsx @@ -157,7 +157,7 @@ test('createServerFn overrides properties', () => { .validator( () => ({ - input: 'a', + input: 'a' as 'a' | 'b' | 'c', }) as const, ) .client(({ context, next }) => { @@ -167,7 +167,7 @@ test('createServerFn overrides properties', () => { return next({ sendContext: newContext, context: newContext }) }) .server(({ data, context, next }) => { - expectTypeOf(data).toEqualTypeOf<{ readonly input: 'a' }>() + expectTypeOf(data).toEqualTypeOf<{ readonly input: 'a' | 'b' | 'c' }>() expectTypeOf(context).toEqualTypeOf<{ readonly context: 'a' @@ -190,7 +190,7 @@ test('createServerFn overrides properties', () => { .validator( () => ({ - input: 'b', + input: 'b' as 'b' | 'c', }) as const, ) .client(({ context, next }) => {