TypeScript shows incompatible types error when sharing same dependency between local packages in monorepo

I’m working with a monorepo setup where I have multiple local packages that aren’t published to NPM but are installed using file paths like npm install ../path/to/package.

The Problem:
I have two TypeScript packages (let’s call them PackageX and PackageY) that both depend on the exact same version of a third-party library with built-in types. When PackageX exports a class that expects a type from this shared dependency, and PackageY tries to use it, TypeScript throws compatibility errors.

Example Code:
In PackageX:

class DatabaseHandler {
    constructor(dbCollection: Collection) {}
}

In PackageY:

const myCollection = database.collection('users')
const handler = new DatabaseHandler(myCollection)

The Error:
TypeScript complains that the Collection type from PackageY’s node_modules isn’t compatible with the Collection type from PackageX’s node_modules, even though they’re identical versions.

Why does TypeScript treat these as different types when they come from the same package version? How can I resolve this without restructuring my entire monorepo?

Same headache here with local packages. Quick fix: add the shared dependency as a peerDependency in both packages instead of a regular dependency. TypeScript will then look for the type in your app’s node_modules instead of each package’s own modules.

ya, this is common in monorepos! typescript sees each local package as its own node_modules. try using pnpm, it manages shared dependencies way better. alternatively, you could use path mapping in your tsconfig to align the type refs. good luck!

I’ve encountered this issue frequently, and it’s quite challenging. TypeScript’s handling of local packages as separate namespaces results in different type identities, even for identical versions of libraries. While workspace hoisting might seem like a solution, it can introduce other problems. A more effective approach is to create a shared types package at the root of your monorepo that re-exports the common types you need. Then, have both PackageX and PackageY import from this shared package, ensuring that TypeScript recognizes them as coming from the same source. You’ll need to set up path mapping in your tsconfig files, but this method is typically more reliable than relying on hoisting.

This happens because TypeScript uses nominal typing for module declarations - it cares about where type definitions physically live, not just if they match structurally. Even identical package versions create separate type identities when they’re in different node_modules folders. I hit this exact problem in a financial services monorepo with shared database types. What worked best was hoisting all shared dependencies to the root package.json using npm/yarn workspaces properly. You end up with just one instance of each dependency across the whole monorepo. You could also try using baseUrl and paths in your tsconfig for module resolution, but workspace hoisting is way more reliable. It actually fixes the problem instead of just working around it.