trpc: bug: inference errors in middleware and context
Provide environment information
System: OS: macOS 13.6.2 CPU: (10) arm64 Apple M1 Pro Memory: 117.69 MB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.15.0 - ~/.nvm/versions/node/v18.15.0/bin/node Yarn: 1.22.19 - ~/.nvm/versions/node/v18.15.0/bin/yarn npm: 9.5.0 - ~/.nvm/versions/node/v18.15.0/bin/npm pnpm: 8.2.0 - ~/Library/pnpm/pnpm Browsers: Brave Browser: 115.1.56.20 Chrome: 119.0.6045.123 Chrome Canary: 121.0.6127.0 Safari: 17.1 npmPackages: @trpc/client: 10.43.2 => 10.43.2 @trpc/react-query: 10.43.2 => 10.43.2 @trpc/server: 10.43.2 => 10.43.2 typescript: ^5.2.2 => 5.2.2
Describe the bug
Hard to explain but I have this code: (create-context.ts)
import {inferAsyncReturnType} from '@trpc/server'
import type {
CreateFastifyContextOptions,
FastifyTRPCPluginOptions,
// eslint-disable-next-line
} from '@trpc/server/adapters/fastify'
/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
export interface CreateInnerContextOptions extends Partial<CreateFastifyContextOptions> {}
export function createContext(config: Partial<FastifyTRPCPluginOptions<any>>) {
return (outerOptions: CreateFastifyContextOptions) => {
return createInternalContext(async (innerOptions) => {
if (!outerOptions?.req || !outerOptions?.res) {
return innerOptions
}
return {
...innerOptions,
...(await config?.trpcOptions?.createContext?.({
req: outerOptions.req,
res: outerOptions.res,
})),
}
}, outerOptions)
}
}
/**
* Outer context. Used in the routers and will e.g. bring `req` & `res` to the context as "not `undefined`".
*
* @see https://trpc.io/docs/context#inner-and-outer-context
*/
async function createInternalContext<T>(
createContextInner: (opts?: CreateInnerContextOptions) => T,
opts: CreateFastifyContextOptions,
) {
const contextInner = await createContextInner()
return {
...contextInner,
req: opts.req,
res: opts.res,
}
}
type LocalContext<T> = inferAsyncReturnType<typeof createInternalContext<T>>
export type Context<T> = T extends infer R ? LocalContext<R> : never
And this wrapper for creating trpc (make-trpc.ts):
import {initTRPC, TRPCError} from '@trpc/server'
import {authenticate} from '@mama/auth-plugin'
import {Context} from './create-context.js'
import {CreateFastifyContextOptions} from '@trpc/server/adapters/fastify'
export function makeTRPC<T>(config?: Parameters<typeof initTRPC.create>[0]) {
const t = initTRPC.context<Context<T>>().create({
errorFormatter(error) {
return config?.errorFormatter?.(error) ?? error.shape
},
})
function withAuth(authOptions: Parameters<typeof authenticate>[1]) {
return t.middleware(async (opts) => {
if (!(opts.ctx as any).req) {
throw new TRPCError({code: 'INTERNAL_SERVER_ERROR', message: 'missing req object'})
}
try {
const {auth} = await authenticate(
(opts.ctx as Context<CreateFastifyContextOptions>).req,
authOptions,
)
return opts.next({
ctx: {
...(opts.ctx as Context<Partial<CreateFastifyContextOptions>>),
auth,
},
})
} catch (e) {
throw new TRPCError({code: 'UNAUTHORIZED'})
}
})
}
return {
router: t.router,
publicProcedure: t.procedure,
createProtectedProcedure: (options: Parameters<typeof authenticate>[1] = {}) =>
t.procedure.use(withAuth(options)),
}
}
This works fine in @trpc/server version 10.43.2, however, when upgrading to 10.43.3 I am getting the following error:
TS2345: Argument of type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
is not assignable to parameter of type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
Type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
is not assignable to type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
. Two different types with this name exist, but they are unrelated.
Types of property unstable_pipe are incompatible.
Type
<$Params extends import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/procedure").ProcedureParams<import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").AnyRootConfig, unknown, unknown, unknown, unknown, unknow...
is not assignable to type
<$Params extends import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/procedure").ProcedureParams<import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").AnyRootConfig, unknown, unknown, unknown, unknown, unknow...
. Two different types with this name exist, but they are unrelated.
Types of parameters fn and fn are incompatible.
Type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
is not assignable to type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
. Two different types with this name exist, but they are unrelated.
Type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
is not assignable to type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
. Two different types with this name exist, but they are unrelated.
Type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
is not assignable to type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareBuilder<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/pa...
. Two different types with this name exist, but they are unrelated.
Types of property _middlewares are incompatible.
Type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareFunction<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/p...
is not assignable to type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareFunction<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/p...
. Two different types with this name exist, but they are unrelated.
Type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareFunction<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/p...
is not assignable to type
import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/middleware").MiddlewareFunction<{ _config: import("/Users/omrikatz/Public/web/mono/packages/trpc-commons/node_modules/@trpc/server/dist/core/internals/config").RootConfig<{ ctx: import("/Users/omrikatz/Public/web/mono/p...
. Two different types with this name exist, but they are unrelated.
Types of parameters opts and opts are incompatible.
Type '{ ctx: Simplify<Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, { ...; }>>; ... 5 more ...; next: { ...; }; }' is not assignable to type '{ ctx: Simplify<Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, Overwrite<...>>>; ... 5 more ...; next: { ...; }; }'.
Types of property ctx are incompatible.
Type 'Simplify<Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, { ...; }>>' is not assignable to type 'Simplify<Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, Overwrite<...>>>'.
Type '((any[] | Date) & Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, { ...; }>) | { [K in keyof Overwrite<...>]: Overwrite<...>[K]; }' is not assignable to type 'Simplify<Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, Overwrite<...>>>'.
Type 'any[] & Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, { ...; }>' is not assignable to type 'Simplify<Overwrite<FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] extends object ? FlatOverwrite<object, { ctx: Unwrap<Context<T>>; }>["ctx"] : object, Overwrite<...>>>'.
Link to reproduction
https://stackblitz.com/github/trpc/examples-next-minimal-starter
To reproduce
Sorry wasn’t able to easily reproduce it in a new repo.
Additional information
No response
👨👧👦 Contributing
- 🙋♂️ Yes, I’d be down to file a PR fixing this bug!
Funding
- You can sponsor this specific effort via a Polar.sh pledge below
- We receive the pledge once the issue is completed & verified
About this issue
- Original URL
- State: closed
- Created 8 months ago
- Reactions: 2
- Comments: 41 (22 by maintainers)
Commits related to this issue
- roll back tRPC to 10.43.2 due to trpc/trpc#5037 — committed to weareinreach/InReach by JoeKarow 8 months ago
Please try out
10.43.4and open a new issue if you encounter anything related 🙏Closing this, feel free to ask to reopen if the problem still persists; the solution above (or the alternative by Nick) should work
@jussisaurio you are awesome man! thanks again
To be clear, it’s the change in tRPC that is causing it. It is just manifesting when using the sentry middleware. For example @omridevk is not using Sentry and experiencing a similar issue. Hopefully the linked PR resolves both.
I managed to repro this now. @sentry/node (whose trpc middleware you are using) is also constructing trpc middlewares using an unconstrained type parameter
Twhich I think is something that both of your problems share. I’ll post a PR with reproing test soonEDIT: @JoeKarow I arrived at this conclusion because I think you’re experiencing errors with extending your
baseProcedurewhich uses the Sentry middleware, but doing e.g.t.procedure.use(isAuthed)works fine.@jussisaurio I can try and make a minimal repro a bit later.
So I think the big pain is with typescript here, t.middleware has never been particularly good because Typescript has problems inferring its inputs/outputs properly in the chain. Partly this is related to the complexity of the types which v11 is going to dramatically improve. Lots of efforts to make this better but having done a 1000 line test file and project around this earlier in the year and abandoning it I suggest a different pattern for this type of thing
https://stackblitz.com/edit/github-nqqlsq-shycwu?file=src%2Fserver%2Fmake-trpc.ts