I am in a bit of a weird situation. For the past 2 weeks I've been trying to debug as to why I am losing types between my projects inside a monorepo. My backend exposes the types that my client uses, but for some reason, certain types just don't get across and become any
. This has made me unable to develop anything on this project for a while. I made a sample repo out of the issue to further showcase it.
The project is built with Yarn Workspaces
and it's structured as following
apps/site
, the NextJS client importing the tRPCAppRouter
apps/backend
, the express backend that is exposing theAppRouter
apps/config
, here are the basetsconfig
s used throught the projectpackages/frontend-shared
, not important for this issue, shared UI components
The problem can be found inside the client in the apps/site/src/lib/ApiProvider.ts
// The type is imported directly from backend, here we use type alias to make it cleaner
import type { AppRouter, EmailType, ProfileType, Test } from "@company/backend/trpc";
export type { AppRouter } from "@company/backend/trpc";
import { inferProcedureOutput } from "@trpc/server";
// The type is inferred to any
// Also if you hover over the app router, the context is also any
type loginOutputType = inferProcedureOutput<AppRouter["user"]["login"]>;
//Profile type doesn't have test field but it lets me set it
const a: ProfileType = {};
a.test = false;
//Same as well here, but it errors out as it should
const b: EmailType = {};
b.test = false;
//
const t: Test = {}
The types for tRPC
method output are inferred to any
for some reason, the const a
type is alias to Profile
but the type checker doesn't complain even if I add fields that don't exist.
The const b
and const t
have correct typing
My setup is pretty standard as far as the typescript configuration, I use this base tsconfig
which sets some sane defaults like strict
and all of the other configs inherit from it
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"moduleResolution": "node",
"preserveWatchOutput": true,
"skipLibCheck": true,
"noUncheckedIndexedAccess": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": false
},
"exclude": ["node_modules"]
}
I've tried tinkering with the tsconfigs, redoing them entirely, tried deleting path aliases, cleaning the yarn cache, tried using project references from frontend to backend but I kept having the same issue
It's very difficult to debug why as there is only typescript magic happening here, no errors or anything of sorts I can look into, I followed the tRPC
setup guide but for some reason, some setting or causing types to be broken.
I am 90% sure the issue is not in fact the tsconfig
's as I also copied entire setups from other people and it still resulted in the same type inference. I have no idea what else affects typescript in this way, my last resort seems to be to make the API layer into a package and use directly import it inside my packages, but that's hacky and would require quite a bit of refactoring, while I am 100% certain that my current setup should indeed work
infer
in one of my custom types. – Angelesangelfish