Skip to content

orpc output type safety #1532

@CaoKha

Description

@CaoKha

Environment

  • @orpc/server: 1.13.14
  • TypeScript: 7.0.0-dev (tsgo)
  • Node.js: 24.13.0
  • OS: macOS (Darwin 25.4.0)

Reproduction

import { oc } from '@orpc/contract'
import { z } from 'zod'
import { os } from '@orpc/server'

const contract = oc.output(z.object({ name: z.string() }))

const procedure = os.contract(contract).handler(async () => {
  // No TS error here, even though `age` is not in the output schema
  return { name: 'Alice', age: 30 }
})

The handler returns { name: 'Alice', age: 30 } but the contract only defines { name: string }. TypeScript does not flag age as an excess property. At runtime, Zod strips age from the response, so the client never receives it.

Describe the bug

Handler return types are not strictly checked against the contract output schema at compile time. When a handler returns extra properties not defined in the output schema, TypeScript does not report an error. The extra fields are silently stripped at runtime by Zod's .strip() behavior, which can lead to subtle bugs that are hard to catch.

Additional context

This is fundamentally a TypeScript limitation (structural typing allows supersets), but frameworks like oRPC could enforce stricter types using utility types like Exact<T> or StrictOmit patterns to catch this at compile time.

Logs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinginvalidThis doesn't seem right

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions