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<...>>>'.
CleanShot 2023-11-14 at 17 05 09@2x

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
<picture> <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/trpc/trpc/issues/5037/pledge.svg?darkmode=1"> Fund with Polar </picture>

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Reactions: 2
  • Comments: 41 (22 by maintainers)

Commits related to this issue

Most upvoted comments

Please try out 10.43.4 and 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 T which I think is something that both of your problems share. I’ll post a PR with reproing test soon

EDIT: @JoeKarow I arrived at this conclusion because I think you’re experiencing errors with extending your baseProcedure which uses the Sentry middleware, but doing e.g. t.procedure.use(isAuthed) works fine.

@JoeKarow you wouldn’t happen to have a compact example of what stopped working in your project after upgrading to 10.43.3?

@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

image