diff --git a/packages/server/src/builder-variants.test-d.ts b/packages/server/src/builder-variants.test-d.ts index 45bd355d3..42fd434b2 100644 --- a/packages/server/src/builder-variants.test-d.ts +++ b/packages/server/src/builder-variants.test-d.ts @@ -99,8 +99,8 @@ describe('BuilderWithMiddlewares', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match @@ -358,8 +358,8 @@ describe('ProcedureBuilder', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match @@ -552,8 +552,8 @@ describe('ProcedureBuilderWithInput', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match @@ -600,8 +600,8 @@ describe('ProcedureBuilderWithInput', () => { input => ({ invalid: true }), ) - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { })).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => {}) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({}), input => ({ mapped: true })) // @ts-expect-error --- output is not match @@ -789,8 +789,8 @@ describe('ProcedureBuilderWithOutput', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match @@ -970,8 +970,8 @@ describe('ProcedureBuilderWithInputOutput', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match @@ -1018,8 +1018,8 @@ describe('ProcedureBuilderWithInputOutput', () => { input => ({ invalid: true }), ) - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { })).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => {}) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({}), input => ({ mapped: true })) // @ts-expect-error --- output is not match @@ -1163,8 +1163,8 @@ describe('RouterBuilder', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match diff --git a/packages/server/src/builder-variants.ts b/packages/server/src/builder-variants.ts index 2ff4f45a2..5ea42b0f6 100644 --- a/packages/server/src/builder-variants.ts +++ b/packages/server/src/builder-variants.ts @@ -1,6 +1,6 @@ import type { AnySchema, ContractRouter, ErrorMap, HTTPPath, InferSchemaInput, InferSchemaOutput, MergedErrorMap, Meta, Route, Schema } from '@orpc/contract' import type { BuilderDef } from './builder' -import type { Context, ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { Context, MergedCurrentContext, MergedInitialContext } from './context' import type { ORPCErrorConstructorMap } from './error' import type { Lazy } from './lazy' import type { MapInputMiddleware, Middleware } from './middleware' @@ -32,22 +32,21 @@ export interface BuilderWithMiddlewares< 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, unknown, unknown, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & BuilderWithMiddlewares< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): BuilderWithMiddlewares< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'meta'( meta: TMeta, @@ -105,22 +104,21 @@ export interface ProcedureBuilder< 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, unknown, unknown, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & ProcedureBuilder< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureBuilder< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'meta'( meta: TMeta, @@ -159,26 +157,25 @@ export interface ProcedureBuilderWithInput< 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, InferSchemaOutput, unknown, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & ProcedureBuilderWithInput< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureBuilderWithInput< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, UInput, unknown, @@ -186,15 +183,14 @@ export interface ProcedureBuilderWithInput< TMeta >, mapInput: MapInputMiddleware, UInput>, - ): ContextExtendsGuard - & ProcedureBuilderWithInput< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureBuilderWithInput< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'meta'( meta: TMeta, @@ -236,22 +232,21 @@ export interface ProcedureBuilderWithOutput< 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, unknown, InferSchemaInput, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & ProcedureBuilderWithOutput< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureBuilderWithOutput< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'meta'( meta: TMeta, @@ -286,26 +281,25 @@ export interface ProcedureBuilderWithInputOutput< 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, InferSchemaOutput, InferSchemaInput, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & ProcedureBuilderWithInputOutput< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureBuilderWithInputOutput< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, UInput, InferSchemaInput, @@ -313,15 +307,14 @@ export interface ProcedureBuilderWithInputOutput< TMeta >, mapInput: MapInputMiddleware, UInput>, - ): ContextExtendsGuard - & ProcedureBuilderWithInputOutput< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureBuilderWithInputOutput< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'meta'( meta: TMeta, @@ -350,20 +343,19 @@ export interface RouterBuilder< 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, unknown, unknown, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & RouterBuilder< - MergedInitialContext, - MergedCurrentContext, - TErrorMap, - TMeta - > + ): RouterBuilder< + MergedInitialContext, + MergedCurrentContext, + TErrorMap, + TMeta + > 'prefix'(prefix: HTTPPath): RouterBuilder diff --git a/packages/server/src/builder.test-d.ts b/packages/server/src/builder.test-d.ts index 6392719cc..5e33d79f1 100644 --- a/packages/server/src/builder.test-d.ts +++ b/packages/server/src/builder.test-d.ts @@ -173,18 +173,15 @@ describe('Builder', () => { }) }), ).toEqualTypeOf< - DecoratedMiddleware, BaseMeta> + DecoratedMiddleware >() - - // @ts-expect-error --- conflict context - builder.middleware(({ next }) => next({ db: 123 })) }) it('can type input and output', () => { expectTypeOf( builder.middleware(({ next }, input: 'input', output: MiddlewareOutputFn<'output'>) => next()), ).toEqualTypeOf< - DecoratedMiddleware, 'input', 'output', ORPCErrorConstructorMap, BaseMeta> + DecoratedMiddleware, 'input', 'output', any, BaseMeta> >() }) }) @@ -241,62 +238,14 @@ describe('Builder', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- Invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match builder.use(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({})) }) - it('with map input', () => { - const applied = builder.use(({ context, next, path, procedure, errors, signal }, input: { mapped: boolean }, output) => { - expectTypeOf(input).toEqualTypeOf<{ mapped: boolean }>() - expectTypeOf(context).toEqualTypeOf() - expectTypeOf(path).toEqualTypeOf() - expectTypeOf(procedure).toEqualTypeOf< - Procedure - >() - expectTypeOf(output).toEqualTypeOf>() - expectTypeOf(errors).toEqualTypeOf>() - expectTypeOf(signal).toEqualTypeOf>() - - return next({ - context: { - extra: true, - }, - }) - }, (input) => { - expectTypeOf(input).toEqualTypeOf() - - return { mapped: true } - }) - - expectTypeOf(applied).toEqualTypeOf< - BuilderWithMiddlewares< - InitialContext & Record, - Omit & { extra: boolean }, - typeof inputSchema, - typeof outputSchema, - typeof baseErrorMap, - BaseMeta - > - >() - - builder.use( - ({ context, next, path, procedure, errors, signal }, input: { mapped: boolean }, output) => next(), - // @ts-expect-error --- invalid map input - input => ({ invalid: true }), - ) - - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { })).toEqualTypeOf() - // @ts-expect-error --- input is not match - builder.use(({ next }, input: 'invalid') => next({}), input => ({ mapped: true })) - // @ts-expect-error --- output is not match - builder.use(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({}), input => ({ mapped: true })) - }) - it('with TInContext', () => { const mid = {} as Middleware<{ cacheable?: boolean } & Record, Record, unknown, unknown, ORPCErrorConstructorMap, BaseMeta> @@ -310,17 +259,6 @@ describe('Builder', () => { BaseMeta > >() - - expectTypeOf(builder.use(mid, () => { })).toEqualTypeOf< - BuilderWithMiddlewares< - InitialContext & { cacheable?: boolean }, - Omit & Record, - typeof inputSchema, - typeof outputSchema, - typeof baseErrorMap, - BaseMeta - > - >() }) }) diff --git a/packages/server/src/builder.test.ts b/packages/server/src/builder.test.ts index d3b760bec..9f603c336 100644 --- a/packages/server/src/builder.test.ts +++ b/packages/server/src/builder.test.ts @@ -167,6 +167,7 @@ describe('builder', () => { mapInput: (map: any) => [mid, map] as any, }) as any) + // @ts-expect-error --- this builder support .use with map input but not type const applied = builder.use(mid2, map2) expect(applied).instanceOf(Builder) diff --git a/packages/server/src/builder.ts b/packages/server/src/builder.ts index 10d91e8f6..69bb0aac1 100644 --- a/packages/server/src/builder.ts +++ b/packages/server/src/builder.ts @@ -1,6 +1,6 @@ import type { AnySchema, ContractProcedureDef, ContractRouter, ErrorMap, HTTPPath, MergedErrorMap, Meta, Route, Schema } from '@orpc/contract' import type { BuilderWithMiddlewares, ProcedureBuilder, ProcedureBuilderWithInput, ProcedureBuilderWithOutput, RouterBuilder } from './builder-variants' -import type { Context, ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { Context, MergedCurrentContext, MergedInitialContext } from './context' import type { ORPCErrorConstructorMap } from './error' import type { Lazy } from './lazy' import type { AnyMiddleware, MapInputMiddleware, Middleware } from './middleware' @@ -119,7 +119,7 @@ export class Builder< middleware( // = any here is important to make middleware can be used in any output by default middleware: Middleware, TMeta>, - ): DecoratedMiddleware, TMeta> { // ORPCErrorConstructorMap ensures middleware can used in any procedure + ): DecoratedMiddleware { // any ensures middleware can used in any procedure return decorateMiddleware(middleware) } @@ -134,42 +134,21 @@ export class Builder< use( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, unknown, unknown, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & BuilderWithMiddlewares< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > - - use( - middleware: Middleware< - UInContext, - UOutContext, - UInput, - unknown, - ORPCErrorConstructorMap, - TMeta - >, - mapInput: MapInputMiddleware, - ): ContextExtendsGuard - & BuilderWithMiddlewares< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): BuilderWithMiddlewares< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > use( middleware: AnyMiddleware, @@ -191,7 +170,7 @@ export class Builder< return new Builder({ ...this['~orpc'], meta: mergeMeta(this['~orpc'].meta, meta), - }) + }) as any } route( @@ -200,7 +179,7 @@ export class Builder< return new Builder({ ...this['~orpc'], route: mergeRoute(this['~orpc'].route, route), - }) + }) as any } input( diff --git a/packages/server/src/context.test-d.ts b/packages/server/src/context.test-d.ts index d323bf837..c7a83e7e1 100644 --- a/packages/server/src/context.test-d.ts +++ b/packages/server/src/context.test-d.ts @@ -1,4 +1,4 @@ -import type { ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { MergedCurrentContext, MergedInitialContext } from './context' it('MergedInitialContext', () => { expectTypeOf>().toMatchTypeOf<{ a: string, b: number }>() @@ -10,30 +10,3 @@ it('MergedCurrentContext', () => { expectTypeOf>().toMatchTypeOf<{ a: string, b: number }>() expectTypeOf>().toMatchTypeOf<{ a: number }>() }) - -interface Empty { - -} - -it('ContextExtendsGuard', () => { - expectTypeOf < ContextExtendsGuard< { a: string, b: string }, { a: string }>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf < ContextExtendsGuard< { a: string, b: string }, Empty>>().toEqualTypeOf() - expectTypeOf < ContextExtendsGuard< { a: string, b: string }, Record>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - - expectTypeOf < ContextExtendsGuard < { a: string }, { a: string, b: string }>>().toEqualTypeOf() - expectTypeOf < ContextExtendsGuard < { a: number }, { a: string }>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - - /** - * We can use `& Record` to deal with `has no properties in common with type` error - */ - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>>().toEqualTypeOf() -}) diff --git a/packages/server/src/context.ts b/packages/server/src/context.ts index 3826c73f9..45fb73326 100644 --- a/packages/server/src/context.ts +++ b/packages/server/src/context.ts @@ -14,5 +14,3 @@ export function mergeCurrentContext( ): MergedCurrentContext { return { ...context, ...other } } - -export type ContextExtendsGuard = T extends U ? unknown : never diff --git a/packages/server/src/implementer-procedure.test-d.ts b/packages/server/src/implementer-procedure.test-d.ts index ea86ed7d3..e4c9dcd9d 100644 --- a/packages/server/src/implementer-procedure.test-d.ts +++ b/packages/server/src/implementer-procedure.test-d.ts @@ -90,14 +90,14 @@ describe('ImplementedProcedure', () => { > >() - // invalid TInContext - expectTypeOf(implemented.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + implemented.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match implemented.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match implemented.use(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({})) - // conflict context - expectTypeOf(implemented.use(({ next }) => next({ context: { db: undefined } }))).toEqualTypeOf() + // @ts-expect-error --- conflict context + implemented.use(({ next }) => next({ context: { db: undefined } })) }) it('with map input', () => { @@ -129,14 +129,14 @@ describe('ImplementedProcedure', () => { > >() - // invalid TInContext - expectTypeOf(implemented.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => {})).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + implemented.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { }) // @ts-expect-error --- input is not match implemented.use(({ next }, input: 'invalid') => next({}), () => {}) // @ts-expect-error --- output is not match implemented.use(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({}), () => {}) - // conflict context but not detected - expectTypeOf(implemented.use(({ next }) => next({ context: { db: undefined } }), () => {})).toEqualTypeOf() + // @ts-expect-error --- conflict context + implemented.use(({ next }) => next({ context: { db: undefined } }), () => { }) }) it('with TInContext', () => { @@ -265,8 +265,8 @@ describe('ProcedureImplementer', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match @@ -313,8 +313,8 @@ describe('ProcedureImplementer', () => { input => ({ invalid: true }), ) - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => {})).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { }) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({}), input => ({ mapped: true })) // @ts-expect-error --- output is not match diff --git a/packages/server/src/implementer-procedure.ts b/packages/server/src/implementer-procedure.ts index 7344d1d53..47568ccc7 100644 --- a/packages/server/src/implementer-procedure.ts +++ b/packages/server/src/implementer-procedure.ts @@ -1,8 +1,8 @@ import type { ClientContext, ClientRest } from '@orpc/client' import type { AnySchema, ErrorMap, InferSchemaInput, InferSchemaOutput, Meta } from '@orpc/contract' -import type { MaybeOptionalOptions } from '@orpc/shared' +import type { IntersectPick, MaybeOptionalOptions } from '@orpc/shared' import type { BuilderDef } from './builder' -import type { Context, ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { Context, MergedCurrentContext, MergedInitialContext } from './context' import type { ORPCErrorConstructorMap } from './error' import type { MapInputMiddleware, Middleware } from './middleware' import type { Procedure, ProcedureHandler } from './procedure' @@ -19,29 +19,27 @@ export interface ImplementedProcedure< TErrorMap extends ErrorMap, TMeta extends Meta, > extends Procedure { - use( + use, UInContext extends Context = TCurrentContext>( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, InferSchemaOutput, InferSchemaInput, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & ContextExtendsGuard, TCurrentContext> - & ImplementedProcedure< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ImplementedProcedure< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > - use( + use, UInput, UInContext extends Context = TCurrentContext>( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, UInput, InferSchemaInput, @@ -49,16 +47,14 @@ export interface ImplementedProcedure< TMeta >, mapInput: MapInputMiddleware, UInput>, - ): ContextExtendsGuard - & ContextExtendsGuard, TCurrentContext> - & ImplementedProcedure< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ImplementedProcedure< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > /** * Make this procedure callable (works like a function while still being a procedure). @@ -110,26 +106,25 @@ export interface ProcedureImplementer< 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, InferSchemaOutput, InferSchemaInput, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & ProcedureImplementer< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureImplementer< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'use'( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, UInput, InferSchemaInput, @@ -137,15 +132,14 @@ export interface ProcedureImplementer< TMeta >, mapInput: MapInputMiddleware, UInput>, - ): ContextExtendsGuard - & ProcedureImplementer< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): ProcedureImplementer< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > 'handler'( handler: ProcedureHandler, InferSchemaInput, TErrorMap, TMeta>, diff --git a/packages/server/src/implementer-variants.test-d.ts b/packages/server/src/implementer-variants.test-d.ts index 27c756180..e5c9e513f 100644 --- a/packages/server/src/implementer-variants.test-d.ts +++ b/packages/server/src/implementer-variants.test-d.ts @@ -47,8 +47,8 @@ describe('ImplementerWithMiddlewares', () => { > >() - // invalid TInContext - expectTypeOf(implementer.nested.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + implementer.nested.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match implementer.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match diff --git a/packages/server/src/implementer-variants.ts b/packages/server/src/implementer-variants.ts index 201ed26bb..e65bb65db 100644 --- a/packages/server/src/implementer-variants.ts +++ b/packages/server/src/implementer-variants.ts @@ -1,5 +1,5 @@ import type { AnyContractRouter, ContractProcedure, InferContractRouterErrorMap, InferContractRouterMeta } from '@orpc/contract' -import type { Context, ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { Context, MergedCurrentContext, MergedInitialContext } from './context' import type { ORPCErrorConstructorMap } from './error' import type { ProcedureImplementer } from './implementer-procedure' import type { Lazy } from './lazy' @@ -14,19 +14,18 @@ export interface RouterImplementerWithMiddlewares< > { use( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, unknown, unknown, ORPCErrorConstructorMap>, InferContractRouterMeta >, - ): ContextExtendsGuard - & ImplementerInternalWithMiddlewares< - T, - MergedInitialContext, - MergedCurrentContext - > + ): ImplementerInternalWithMiddlewares< + T, + MergedInitialContext, + MergedCurrentContext + > router>( router: U): EnhancedRouter> diff --git a/packages/server/src/implementer.test-d.ts b/packages/server/src/implementer.test-d.ts index 7485a341b..789ee5662 100644 --- a/packages/server/src/implementer.test-d.ts +++ b/packages/server/src/implementer.test-d.ts @@ -63,13 +63,10 @@ describe('Implementer', () => { { extra: boolean }, unknown, any, - ORPCErrorConstructorMap, + any, Meta | BaseMeta > >() - - // @ts-expect-error --- conflict context - implementer.middleware(({ next }) => next({ db: 123 })) }) it('can type input and output', () => { @@ -81,7 +78,7 @@ describe('Implementer', () => { Record, 'input', 'output', - ORPCErrorConstructorMap, + any, Meta | BaseMeta > >() @@ -116,8 +113,8 @@ describe('Implementer', () => { > >() - // invalid TInContext - expectTypeOf(implementer.nested.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + implementer.nested.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match implementer.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match diff --git a/packages/server/src/implementer.ts b/packages/server/src/implementer.ts index c0c47e6da..c72e6755e 100644 --- a/packages/server/src/implementer.ts +++ b/packages/server/src/implementer.ts @@ -1,6 +1,6 @@ import type { AnyContractRouter, ContractProcedure, InferContractRouterErrorMap, InferContractRouterMeta } from '@orpc/contract' import type { AnyFunction } from '@orpc/shared' -import type { Context, ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { Context, MergedCurrentContext, MergedInitialContext } from './context' import type { ORPCErrorConstructorMap } from './error' import type { ProcedureImplementer } from './implementer-procedure' import type { ImplementerInternalWithMiddlewares } from './implementer-variants' @@ -30,23 +30,22 @@ export interface RouterImplementer< ORPCErrorConstructorMap>, InferContractRouterMeta >, - ): DecoratedMiddleware, InferContractRouterMeta> // ORPCErrorConstructorMap ensures middleware can used in any procedure + ): DecoratedMiddleware> // any ensures middleware can used in any procedure use( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, unknown, unknown, ORPCErrorConstructorMap>, InferContractRouterMeta >, - ): ContextExtendsGuard - & ImplementerInternalWithMiddlewares< - T, - MergedInitialContext, - MergedCurrentContext - > + ): ImplementerInternalWithMiddlewares< + T, + MergedInitialContext, + MergedCurrentContext + > router>( router: U): EnhancedRouter> diff --git a/packages/server/src/middleware-decorated.test-d.ts b/packages/server/src/middleware-decorated.test-d.ts index 34d83a178..0a62a4d20 100644 --- a/packages/server/src/middleware-decorated.test-d.ts +++ b/packages/server/src/middleware-decorated.test-d.ts @@ -79,8 +79,8 @@ describe('DecoratedMiddleware', () => { > >() - // invalid TInContext - expectTypeOf(decorated.concat({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + decorated.concat({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- output is not match decorated.concat(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({})) }) @@ -125,8 +125,8 @@ describe('DecoratedMiddleware', () => { input => ({ invalid: true }), ) - // invalid TInContext - expectTypeOf(decorated.concat({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { })).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + decorated.concat({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { }) // @ts-expect-error --- output is not match decorated.concat(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({}), input => ({ mapped: true })) }) diff --git a/packages/server/src/middleware-decorated.ts b/packages/server/src/middleware-decorated.ts index 3e8c612dc..e716971e0 100644 --- a/packages/server/src/middleware-decorated.ts +++ b/packages/server/src/middleware-decorated.ts @@ -1,5 +1,5 @@ import type { Meta } from '@orpc/contract' -import type { Context, ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { Context, MergedCurrentContext, MergedInitialContext } from './context' import type { ORPCErrorConstructorMap } from './error' import type { AnyMiddleware, MapInputMiddleware, Middleware } from './middleware' @@ -15,24 +15,26 @@ export interface DecoratedMiddleware< map: MapInputMiddleware, ): DecoratedMiddleware - concat>( + concat< + UOutContext extends Context, + UInContext extends Context = MergedCurrentContext, + >( middleware: Middleware< - UInContext, + UInContext | MergedCurrentContext, UOutContext, TInput, TOutput, TErrorConstructorMap, TMeta >, - ): ContextExtendsGuard, UInContext> - & DecoratedMiddleware< - MergedInitialContext>, - MergedCurrentContext, - TInput, - TOutput, - TErrorConstructorMap, - TMeta - > + ): DecoratedMiddleware< + MergedInitialContext>, + MergedCurrentContext, + TInput, + TOutput, + TErrorConstructorMap, + TMeta + > concat< UOutContext extends Context, @@ -40,7 +42,7 @@ export interface DecoratedMiddleware< UInContext extends Context = MergedCurrentContext, >( middleware: Middleware< - UInContext, + UInContext | MergedCurrentContext, UOutContext, UMappedInput, TOutput, @@ -48,15 +50,14 @@ export interface DecoratedMiddleware< TMeta >, mapInput: MapInputMiddleware, - ): ContextExtendsGuard, UInContext> - & DecoratedMiddleware< - MergedInitialContext>, - MergedCurrentContext, - TInput, - TOutput, - TErrorConstructorMap, - TMeta - > + ): DecoratedMiddleware< + MergedInitialContext>, + MergedCurrentContext, + TInput, + TOutput, + TErrorConstructorMap, + TMeta + > } export function decorateMiddleware< diff --git a/packages/server/src/procedure-decorated.test-d.ts b/packages/server/src/procedure-decorated.test-d.ts index d2de10b61..6ecbc06b2 100644 --- a/packages/server/src/procedure-decorated.test-d.ts +++ b/packages/server/src/procedure-decorated.test-d.ts @@ -118,14 +118,14 @@ describe('DecoratedProcedure', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>)).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({})) // @ts-expect-error --- output is not match builder.use(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({})) - // conflict context but not detected - expectTypeOf(builder.use(({ next }) => next({ context: { db: undefined } }))).toEqualTypeOf() + // @ts-expect-error --- conflict context + builder.use(({ next }) => next({ context: { db: undefined } })) }) it('with map input', () => { @@ -157,14 +157,14 @@ describe('DecoratedProcedure', () => { > >() - // invalid TInContext - expectTypeOf(builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => { })).toEqualTypeOf() + // @ts-expect-error --- invalid TInContext + builder.use({} as Middleware<{ auth: 'invalid' }, any, any, any, any, any>, () => {}) // @ts-expect-error --- input is not match builder.use(({ next }, input: 'invalid') => next({}), () => {}) // @ts-expect-error --- output is not match builder.use(({ next }, input, output: MiddlewareOutputFn<'invalid'>) => next({}), () => {}) - // conflict context but not detected - expectTypeOf(builder.use(({ next }) => next({ context: { db: undefined } }), () => {})).toEqualTypeOf() + // @ts-expect-error --- conflict context + builder.use(({ next }) => next({ context: { db: undefined } }), () => { }) }) it('with TInContext', () => { diff --git a/packages/server/src/procedure-decorated.ts b/packages/server/src/procedure-decorated.ts index 43f96e24d..703e05857 100644 --- a/packages/server/src/procedure-decorated.ts +++ b/packages/server/src/procedure-decorated.ts @@ -1,7 +1,7 @@ import type { ClientContext, ClientRest } from '@orpc/client' import type { AnySchema, ErrorMap, InferSchemaInput, InferSchemaOutput, MergedErrorMap, Meta, Route } from '@orpc/contract' -import type { MaybeOptionalOptions } from '@orpc/shared' -import type { Context, ContextExtendsGuard, MergedCurrentContext, MergedInitialContext } from './context' +import type { IntersectPick, MaybeOptionalOptions } from '@orpc/shared' +import type { Context, MergedCurrentContext, MergedInitialContext } from './context' import type { ORPCErrorConstructorMap } from './error' import type { AnyMiddleware, MapInputMiddleware, Middleware } from './middleware' import type { CreateProcedureClientOptions, ProcedureClient } from './procedure-client' @@ -53,29 +53,27 @@ export class DecoratedProcedure< }) } - use( + use, UInContext extends Context = TCurrentContext>( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, InferSchemaOutput, InferSchemaInput, ORPCErrorConstructorMap, TMeta >, - ): ContextExtendsGuard - & ContextExtendsGuard, TCurrentContext> - & DecoratedProcedure< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): DecoratedProcedure< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > - use( + use, UInput, UInContext extends Context = TCurrentContext>( middleware: Middleware< - UInContext, + UInContext | TCurrentContext, UOutContext, UInput, InferSchemaInput, @@ -83,16 +81,14 @@ export class DecoratedProcedure< TMeta >, mapInput: MapInputMiddleware, UInput>, - ): ContextExtendsGuard - & ContextExtendsGuard, TCurrentContext> - & DecoratedProcedure< - MergedInitialContext, - MergedCurrentContext, - TInputSchema, - TOutputSchema, - TErrorMap, - TMeta - > + ): DecoratedProcedure< + MergedInitialContext, + MergedCurrentContext, + TInputSchema, + TOutputSchema, + TErrorMap, + TMeta + > use(middleware: AnyMiddleware, mapInput?: MapInputMiddleware): DecoratedProcedure { const mapped = mapInput diff --git a/packages/shared/src/types.test-d.ts b/packages/shared/src/types.test-d.ts new file mode 100644 index 000000000..4ca2cf2b4 --- /dev/null +++ b/packages/shared/src/types.test-d.ts @@ -0,0 +1,35 @@ +import type { IntersectPick, MaybeOptionalOptions, SetOptional } from './types' + +it('MaybeOptionalOptions', () => { + const a = (...[options]: MaybeOptionalOptions<{ a: number }>) => { + expectTypeOf(options).toEqualTypeOf<{ a: number }>() + } + + // @ts-expect-error - options is required + a() + // @ts-expect-error - options is invalid + a({ a: '1' }) + a({ a: 1 }) + + const b = (...[options]: MaybeOptionalOptions<{ b?: number }>) => { + expectTypeOf(options).toEqualTypeOf<{ b?: number } | undefined>() + } + + b() + // @ts-expect-error - options is invalid + b({ b: '1' }) + b({ b: 1 }) +}) + +it('SetOptional', () => { + expectTypeOf>().toMatchTypeOf<{ a?: number }>() + expectTypeOf>().toMatchTypeOf<{ a?: number }>() +}) + +interface Empty {} + +it('IntersectPick', () => { + expectTypeOf>().toEqualTypeOf<{ a: number }>() + expectTypeOf>().toEqualTypeOf<{ b: number }>() + expectTypeOf>().toEqualTypeOf() +}) diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index c7e113ae6..73deb37ce 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -3,3 +3,5 @@ export type MaybeOptionalOptions = Record extends TOptio : [options: TOptions] export type SetOptional = Omit & Partial> + +export type IntersectPick = Pick