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

Working with relative paths in React Native TypeScript using WebStorm

My variable color is located in the path app/theme. To set it up, I created a package.json file in app/package.json with the following content: { "name": "app" } Now, to import color in TypeScript files, I use the following syntax: import { color } fro ...

Leveraging Next Js with an external REST API for streamlined authentication and authorization functionality

I am currently working on transitioning my existing application that was developed with Node.js and Express, along with a front end built using create-react-app with Redux, to Next.js. However, I have hit a roadblock as I am unsure of the correct method ...

Tips for uploading images in Next.js using Firebase

While following a tutorial on Next.js, I encountered an issue with the outdated version of Firebase being used. Despite trying different solutions from the documentation and various sources, I am still facing an error message while attempting to upload ima ...

Accessing HTTP data through a function instead of using ngOnInit in Angular is a more efficient approach

Trying to retrieve data from a service using setInterval has posed an issue for me. When I call the service from ngOnInit, everything functions as expected. However, when attempting to call it from any other function, an error occurs: "ERROR TypeError: Ca ...

Tips for hiding a bootstrap modal in Angular4 when it has no content

I am currently working on an Angular 4 e-commerce application. One of the requirements is to hide a bootstrap modal when there are no products in the cart. When a user selects some products, they are added to the mycart modal screen. The user also has the ...

Unable to make a reference to this in TypeScript

In my Angular2 application, I have a file upload feature that sends files as byte arrays to a web service. To create the byte array, I am using a FileReader with an onload event. However, I am encountering an issue where I cannot reference my uploadService ...

The retrieved item has not been linked to the React state

After successfully fetching data on an object, I am attempting to assign it to the state variable movie. However, when I log it to the console, it shows as undefined. import React, {useState, useEffect} from "react"; import Topbar from '../H ...

Tips for producing exclusive test coverage solely from a designated testing directory in Cypress

While going through Cypress documentation, I discovered a feature where Cypress can create an index.html file within the coverage/lcov-report/index.html directory. This file contains details about the test coverage across the entire application. There is ...

Issue with Apache 2.4 redirection on load-balanced reverse-proxy, functioning on non-load-balanced reverse-proxy

I currently have 2 NextJS internal servers/applications being reverse-proxied by Apache 2.4. Let's refer to the internal servers as http://internal:3000/foo and http://internal:3001/foo, with the external URL being http://external/foo. When accessing ...

The CldUploadWidget creates an overlay featuring a never-ending loading spinner

Attempting to utilize the CldUploadWidget from the next-cloudinary package in a Next.js app with the app router. The goal is to upload an image and retrieve the public_id and URL, but upon setup, clicking the button that opens the overlay only displays a l ...

Angular 2 Demonstrate Concealing and Revealing an Element

I am currently facing an issue with toggling the visibility of an element based on a boolean variable in Angular 2. Below is the code snippet for showing and hiding the div: <div *ngIf="edited==true" class="alert alert-success alert-dismissible fade i ...

Proper positioning of try/catch block in scenarios involving delayed async/await operations

For the past six months, I have been utilizing async/await and have truly enjoyed the convenience it provides. Typically, I adhere to the traditional usage like so: try { await doSomethingAsync() } catch (e) {} Lately, I've delved into experimenti ...

Encountering Thumbnail Type Error While Implementing Swiper in Next.js

When trying to integrate Swiper with Next.js, I ran into an issue concerning thumbnails. "onSwiper={setThumbsSwiper}" This line is causing a TypeScript error: swiper-react.d.ts(23, 3): The expected type comes from property 'onSwiper' w ...

Exploring the world of typescript with the power of ts-check

I'm having trouble figuring out how to work with a generic function using TypeScript's new ts-check feature. /** * @type {Reducer<IPoiState, any>} */ const poi = handleActions({ [ADD_BOOKMARK_START]: (state) => { return { ...sta ...

The JSX component in next.js cannot be utilized as a user component

I am facing difficulty in getting my mobile menu to function properly. Initially, I attempted to position it above other content using the useEffect hook, but unfortunately, it resulted in breaking the entire project. This is the error message I encountere ...

Can someone explain why the Next 13 API route is showing up as empty?

I am currently working with Next 13 and I am attempting to create a simple API route. My goal is to have a: "hi" returned when I visit localhost:3000/api/auth. Despite not encountering a 404 error or any errors in the terminal or console, I can&a ...

Check the validity of a password using Angular's reactive forms

For my password validation using ng Reactive Forms, I have a basic html input field for the password and warning paragraphs outlining the password requirements. <div class="field"> <label class="label">Password</label ...

Angular's change detection is currently inactive

I need to toggle the visibility of a button based on the value of a boolean variable using the Output property. However, I am facing an issue where the button remains hidden even after the variable is updated with a true value. Parent Component.ts showE ...

What is the correct way to utilize the createAsyncThunk function in TypeScript?

You can access the entire project here. I currently have this code snippet: extraReducers: (builder) => { builder .addCase(getTodosAsync.fulfilled, (state, action:any) => { return action.payload.todos ...

Is it possible to create a class object with properties directly from the constructor, without needing to cast a custom constructor signature

class __Constants__ { [key: string]: string; constructor(values: string[]) { values.forEach((key) => { this[key] = key; }); } } const Constants = __Constants__ as { new <T extends readonly string[]>(values: T): { [k in T[num ...