diff --git a/packages/server/src/router-client.test-d.ts b/packages/server/src/router-client.test-d.ts index 8895dab3d..e6ec1aea7 100644 --- a/packages/server/src/router-client.test-d.ts +++ b/packages/server/src/router-client.test-d.ts @@ -1,9 +1,12 @@ +import type { AnyNestedClient } from '@orpc/client' import type { Schema } from '@orpc/contract' import * as z from 'zod' import { os } from './builder' +import type { Lazyable } from './lazy' import type { ProcedureClient } from './procedure-client' import type { RouterClient } from './router-client' +import { createRouterClient } from './router-client' const errorMap = { BASE: { @@ -58,6 +61,44 @@ describe('RouterClient', () => { >() }) + it('unwraps lazy at any depth', () => { + const lazyRouter = { + ping: os.lazy(() => Promise.resolve({ default: router.ping })), + nested: os.lazy(() => Promise.resolve({ default: { lazy: router.lazy } })), + } + const client = {} as RouterClient + + expectTypeOf(client.ping).toEqualTypeOf< + ProcedureClient + >() + + expectTypeOf(client.nested.lazy.peng).toEqualTypeOf< + ProcedureClient, object> + >() + }) + + it('supports a key that is either lazy or not', () => { + type LazyableRouter = { + ping: Lazyable + nested: Lazyable + } + const client = {} as RouterClient + + expectTypeOf(client.ping).toEqualTypeOf< + ProcedureClient + >() + + expectTypeOf(client.nested.pong).toEqualTypeOf< + ProcedureClient + >() + }) + + it('maps an optional key to never', () => { + type OptionalRouter = { ping?: typeof router.ping } + + expectTypeOf>().toEqualTypeOf<{ ping?: never }>() + }) + it('support single procedure', () => { type P = RouterClient<(typeof router)['ping'], { cache?: boolean }> @@ -66,3 +107,9 @@ describe('RouterClient', () => { >() }) }) + +describe('createRouterClient', () => { + it('accepts an untyped router', () => { + const _client: AnyNestedClient = createRouterClient({} as any, {}) + }) +}) diff --git a/packages/server/src/router-client.ts b/packages/server/src/router-client.ts index 11325f21a..debd93530 100644 --- a/packages/server/src/router-client.ts +++ b/packages/server/src/router-client.ts @@ -6,6 +6,7 @@ import { resolveMaybeOptionalOptions, toArray } from '@orpc/shared' import type { Lazyable } from './lazy' import { Lazy } from './lazy' +import type { ProcedureDefinition } from './procedure' import { Procedure } from './procedure' import type { ProcedureClient, ProcedureClientOptions } from './procedure-client' import { createProcedureClient } from './procedure-client' @@ -19,12 +20,20 @@ import { getRouter } from './router-utils' * * @see {@link https://orpc.dev/docs/client/client-side#creating-a-client | Client-Side Clients - Creating a Client} */ -export type RouterClient = - TRouter extends Procedure - ? ProcedureClient +export type RouterClient< + TRouter extends AnyRouter, + TClientContext extends ClientContext = object, +> = RouterClientInner + +type RouterClientInner = T extends { + '~orpc': ProcedureDefinition +} + ? ProcedureClient + : T extends Lazy + ? RouterClientInner : { - [K in keyof TRouter]: TRouter[K] extends Lazyable - ? RouterClient + [K in keyof T]: T[K] extends Lazyable + ? RouterClientInner : never }