Typescript is missing Zod and tRPC types throughout all projects in the monorepo, leading to the use of 'any'

Recently, I've found myself stuck in a puzzling predicament. For the last couple of weeks, I've been trying to troubleshoot why the types are getting lost within my projects housed in a monorepo. Even though my backend exposes the necessary types for my client, some of them inexplicably end up as any. This issue has effectively halted any progress on this project for a significant amount of time. To illustrate the problem further, I created a sample repository that showcases this issue: check it out here.

The structure of the project involves using Yarn Workspaces, and it is divided into the following components:

  • apps/site: NextJS client importing the tRPC AppRouter
  • apps/backend: Express backend exposing the AppRouter
  • apps/config: Includes the base tsconfigs used across the project
  • packages/frontend-shared: Not directly related to this issue, contains shared UI components

The problematic code can be found within the client's file located at apps/site/src/lib/ApiProvider.ts

// The type is imported directly from backend, employing a type alias for clarity
import type { AppRouter, EmailType, ProfileType, Test } from "@company/backend/trpc";

export type { AppRouter } from "@company/backend/trpc";
import { inferProcedureOutput } from "@trpc/server";

// The type gets inferred as any
// Hovering over the app router also shows any context
type loginOutputType = inferProcedureOutput<AppRouter["user"]["login"]>;
//Profile type lacks the test field but allows setting it without errors
const a: ProfileType = {};
a.test = false;

//Similarly with this case, where it should error out due to missing fields
const b: EmailType = {};
b.test = false;

//
const t: Test = {}

The types for tRPC method output end up being inferred as any for unknown reasons. While const a is an alias for Profile, the type checker fails to flag nonexistent fields. On the other hand, both const b and const t have correct typings.

My TypeScript configuration follows standard practices, and I use this base tsconfig as a template, which sets sensible defaults like strict, with all other configurations inheriting 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 attempted various adjustments such as tweaking the tsconfigs, completely redoing them, deleting path aliases, clearing the yarn cache, experimenting with project references from frontend to backend, yet the issue persists.

Debugging this matter has proven to be quite challenging since there are no specific errors or indicators to investigate, just TypeScript "magic". Although I followed the tRPC setup guide diligently, something seems to be misconfigured or causing issues with type inference.

I'm fairly confident that the root cause isn't the tsconfig itself, especially considering I replicated setups from others only to encounter the same type inference woes. Exploring the option of turning the API layer into a standalone package and directly importing it into my packages feels like a hacky solution that would require extensive refactoring, even though my current setup should theoretically function correctly.

Answer №1

Dealing with a similar issue, I found myself in a monorepo scenario, consisting of 2 packages: trpc-backend and mobile-app. The solution for resolving the problem within the frontend app involved adding the references property to my frontend tsconfig.json:

{
 ...
 "references": [{ "path": "../trpc-backend" }] 
}

Upon doing this, a TypeScript warning surfaced, indicating that the referenced project must have the composite set to true. To address this, it was necessary to navigate to the backend code and include the composite property under compilerOptions:

{
...
"compilerOptions": {
    ...
    "composite": true,
 }
}

With this adjustment, the issue was successfully resolved.

Answer №2

Giving credit to @oae for providing the solution that resolved my issue

The problem I encountered was due to using path aliases in the backend as well. Switching to relative paths in the backend allowed the frontend to recognize the types correctly.

Update: However, I believe there is a more elegant solution available. This involves emitting js output files directly after converting aliased imports into relative ones. This can be accomplished with https://github.com/justkey007/tsc-alias, example:

{
  ...
  "scripts": {
    "build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
   ...

Answer №3

Referring to the response from don duke

The use of project references conflicts with the presence of the noEmit: true flag in the tsconfig.json file, which is required for tools like Webpack, Parcel, and Rollup.

If you are utilizing Zod types declared in a separate package, it is recommended to resolve the typing issue by removing the tsconfig.json file (as suggested in this Turborepo article) from your types package while still maintaining the noEmit: true configuration.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing the Infinite Scroll feature with 'react-infinite-scroll-component' on local JSON data: A step-by-step guide

Currently experimenting with frontEnd development, I have incorporated the 'react-infinite-scroll-component' as a dependency. My goal is to apply this effect to my local Json data without relying on any API calls at this stage. I'm encounter ...

Enable the parsing of special characters in Angular from a URL

Here is a URL with special characters: http://localhost:4200/auth/verify-checking/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="59663c34383035643230383d2b606a6e6b686d6e6e193e34383035773a3634">[email protected]</a> ...

Error: Jest + Typescript does not recognize the "describe" function

When setting up Jest with ts-jest, I encountered the error "ReferenceError: describe is not defined" during runtime. Check out this minimal example for more information: https://github.com/PFight/jest-ts-describe-not-defined-problem I'm not sure what ...

Is it possible to implement a redirect in Angular's Resolve Navigation Guard when an error is encountered from a resolved promise?

I have integrated Angularfire into my Angular project and am utilizing the authentication feature. Everything is functioning properly, however, my Resolve Navigation Guard is preventing the activation of the component in case of an error during the resolve ...

How can you obtain the userID in the Next.js 13 application directory on the server side using next-auth?

Currently, my setup includes: nextJS: 13.1.6 nextAuth 4.19.2 The application is utilizing the new app/ directory, currently in its Beta stage. An issue I am facing involves retrieving user information on a page and performing logic based on that ...

Retrieve the generic type parameter of an interface implementation

I am attempting to extract a type parameter from an interface in order to use it as a parameter for a generic function. In my particular scenario, I have the following generic types: interface ITranslatable<T, K extends keyof T> { translations: IT ...

How can I add a Subresource Integrity Check to a Next.js project? Is it possible in Next.js 12, 13, or 14?

Implementing the experimental sri module. experimental: { sri: { algorithm: 'sha256' } }, The subresource integrity manifest files are generated, however, the integrity attribute is not injected into the build. We were anticipat ...

An Easy Guide to Incorporating react-cookie into TypeScript Projects

I am currently developing an application in React using the React template provided by Visual Studio 2017. My goal is to incorporate react-cookie into my project. After installing this library with the command: npm install react-cookie However, when I at ...

The ngOnChanges method fails to exhibit the anticipated modifications in a variable

Trying to grasp the concept of the ngOnChanges() callback, I created an example below. Despite having values for the attributes title and content in the Post interface during compile time, I do not see any logs from ngOnChanges. Please advise on the corre ...

React and Next.js: Excluding transmission of cookies from server-side to backend

My setup involves using Kubernetes for managing the backend (microservices) and React for the client-side. I am facing an issue with authentication - I can successfully send and authenticate via cookies when the request is initiated from the browser (clien ...

Display the latest item using React

I've encountered a problem with displaying the last message in my pet chat application. Currently, I have to manually scroll to the bottom every time I open the chat in order to see the latest message. This scrolling behavior is not very user-friendly ...

Simulate an HTTP request from a service class during a jasmine test

I initially believed that spying on services in Jasmine using the spyOn method and returning a value when the method is called was straightforward. However, it seems like my assumption may have been too simplistic? I intend to test the following action in ...

Implementing click events to control GSAP animations in Next.js

I'm having trouble figuring out how to pause/start an animation using GSAP in Nextjs. Specifically, I can't seem to work with a tl.current outside the useEffect hook within this component. My goal is that when I click on the first .imgWrapper ele ...

Can someone provide a description for a field within typedoc documentation?

Here is the code snippet: /** * Description of the class */ export class SomeClass { /** * Description of the field */ message: string; } I have tested it on the TSDoc playground and noticed that there is a summary for the class, but not for it ...

Tips for utilizing the Apollo cache consistently during page transitions in NextJs

In the official examples repository of NextJS, I found this apolloClient.js file: import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client' import { concatPagination } from '@apollo/client/utilities' import merge from &apos ...

Angular Nested Interface is a concept that involves defining an

Looking for guidance on creating a nested interface for JSON data like this: Any help is appreciated. JSON Structure "toto": { "toto1": [], "toto2": [], "toto3": [], } Interface Definition export interface Itot ...

Enhance user profiles by enabling secure photo uploads through authentication

After spending 3 days on this, I am still stumped!!! I successfully set up a website using next.js with authentication via auth.js, prisma, and postgres. Everything is functioning smoothly. In addition, I created a profile settings page where users can c ...

During the build process, Next.js disrupts the layout when incorporating Tailwind CSS

Everything looks great on the dashboard layout in development mode. However, once I build the nextjs project, the layout gets all messed up. In production, I'm using Tailwind CSS and Preact. Any assistance you could provide would be greatly apprecia ...

Is it possible to use the HostListener in Angular 7 to detect any scroll event occurring on a specific element within a webpage?

I am developing a breadcrumb bar component in Angular 7 that should dynamically hide and show based on user scrolling behavior. To achieve this, I created a directive to track the scroll position of the container element. While my code for capturing the s ...

How can I receive the response from a GET request using React Query? I am currently only able to get the response

I have created a search component where I input a name in the request, receive a response, and display it immediately. However, after the first input submit, I get undefined in response. Only after the second submit do I get the desired response. The tec ...