trpc: bug: conflicting `ctx` types allowed at compile time

Provide environment information

  System:
    OS: macOS 13.2.1
    CPU: (12) arm64 Apple M2 Max
    Memory: 57.83 GB / 96.00 GB
    Shell: 5.8.1 - /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
  Browsers:
    Chrome: 112.0.5615.137
    Safari: 16.3
"@trpc/server": "10.23.0",

Describe the bug

routers don’t seem to check the types of their procedures or sub-routers. self contained example:

import {initTRPC} from '@trpc/server'
import {createNextApiHandler} from '@trpc/server/adapters/next'

const trpca = initTRPC.context<{a: string}>().create()
const trpcb = initTRPC.context<{b: string}>().create()

const parent = trpca.router({
  a: trpca.procedure.query(({ctx}) => ctx.a.slice()),
  b: trpcb.procedure.query(({ctx}) => ctx.b.slice()),
})

export default createNextApiHandler({
  router: parent,
  createContext: () => ({a: 'aaa'}),
})

this is fine at compile time, and hitting /api/trpc.a works, but hitting /api/trpc.b gets TypeError: Cannot read properties of undefined (reading 'slice') (since ctx.b is never defined anywhere) - I shouldn’t have been allowed to use trpcb.procedure.query(...) on a router for trpca, since they have different contexts.

Link to reproduction

https://www.typescriptlang.org/play?ts=5.0.3#code/JYWwDg9gTgLgBAb2AO2DAKgJQAoGEC+cAZlBCHAOQACMUYAxgPQDOAplAG7sUBQoksRPSisAhjFYA5VgA8YAQTDAAEqOQATADbtCJMpRp0mbTu0aj1osBKjNGyWTF496EZM3i0GouAF44KGhYeAB0rsgScgA8CKIAXHAeUCgA5vgAfAAUAJRhIuKsOS5uHnBe9ABGfgGoGDi4YW6RMDEVCUmpGTl5YhJFxe7wYKIiEdXloiGkAK42mQg8cHDxZUaTYKT0rOrTIiEAjtPsAJ6Z8-QwMvjZfulwFzIhk8yawFs52QA0i3BtqwwVEIbCBbHZ7Q4nM4IB7XW73S4hQEvN6FbJfHjXHg8WQCeDqVhEUTTTTwYS9KSORQqNRadjzH4zGwJYajGDfJZkgq4JqOBI5OHzFYUUQiijXb7XIA

To reproduce

Paste the code above into a next api handler. I tried that above using the stackblitz suggested but there’s a strange compile error that I don’t get locally, it doesn’t seem to acknowledge that the export for the next adapter exists: https://stackblitz.com/edit/github-qwqz1i-qs9elv?file=src/pages/api/trpc/[trpc].ts

Additional information

My use-case: I’m trying to make a router in a library, which can be plugged into an application’s parent router for modular functionality, with the requirement that the parent has a context which meets the requirement of the library router. But the type system doesn’t seem to enforce this. Maybe it’s just not supported (yet)?

I’m putting yes I’d be down to fix this but I don’t know if I’ll have the time to so if someone else wants to, go for it!

👨‍👧‍👦 Contributing

  • 🙋‍♂️ Yes, I’d be down to file a PR fixing this bug!

TRP-29

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/4306/pledge.svg?darkmode=1"> Fund with Polar </picture>

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 1
  • Comments: 15 (10 by maintainers)

Commits related to this issue

Most upvoted comments

Yep, definitely try and use a factory. I’m acutely aware of HOC syndrome reaching tRPC though: withMMKALThing(withLogging(withAuth(t.procedure))) is just not a beautiful thing, so if you have any questions or feedback as you build please bring it back to us. I’m really keen to provide better APIs for library authors 😃

PRs very welcome on banning mixed t instances, so we’ll keep this issue open