Skip to content

Monorepo supportΒ #62962

@valler

Description

@valler

πŸ” Search Terms

skipLibCheck, node_modules, package

βœ… Viability Checklist

⭐ Suggestion

The TypeScript compiler should be aware of package boundaries, especially when it comes to --noEmit type checks.

Basically anything in node_modules should be "none of its business", at least optionally.

The following options give the impression that this is somehow achievable, when in fact it's not doable at all (or maybe only with yet another tool built to work around compiler limitations):

  • moduleResolution
  • resolvePackageJsonExports
  • resolvePackageJsonImports
  • skipLibCheck
  • isolatedModules
  • references in combination with tsc --build --noEmit

Except for browsers, engines these days all support TS in one way or another, and
package managers all support monorepos in one way or another.
During development, there is practically no need (anymore) to compile the source, which would improve DX.
Compilation still is required for browsers, and/or the goal is to reduce shipped code size, or to build a SEA.

However:

Without a compilation step, TSC won't find declaration files and therefore checks source files, treating them as first party code, even if this code is explicitly imported as a third party package (via a specifier that resolves into another package through node_modules, not a relative path that doesn't include node_modules).

Checking of yet uncompiled dependencies in a monorepo will resolve modules from other packages to relative paths / internal dependencies, and therefore a module will be checked as often as it appears as a dependency of a package in the monorepo.

E.g. package a and b depend on c, therefore c will be type checked three times if each package has it's own tsc --noEmit script in its package.json (which it should). That's wasteful, but so is compiling/building dependencies before being able to type check.

Isolated checking of modules and/or packages should be a no-brainer for users. Unfortunately it isn't.

To summarize the feature request:

tsc --noEmit in monorepos should check uncompiled packages only as far as it would with declaration files. The boundary should be the interface imported/used on the call site, without diving deeper into typings of its implementation.

Also note, "monorepo" is mentioned for practical purposes only; what really matters is the package boundary, regardless of monorepo specifics.

The rest are issue references to a subset of those I've read before opening this one, to make sure not to open up an obvious duplicate.

Similar issues, but not focusing on monorepos:

Not as closely related, but also highlights on conflicts between tsc and node_modules resolution:

πŸ“ƒ Motivating Example

import "@scope/module"; // should only be checked on the interface when running `tsc --noEmit`
import "#mapped-to-@scope/module"; // same

// To illustrate the boundary:
export const f = (a: string): number => // signature still matters, if reachable according to api exposed by package.json
  a / 2; // implementation inside body: out of scope for packages depending on this export. Yes, this divides a string.

πŸ’» Use Cases

  1. What do you want to use this for?

    Type checking packages (or modules) in isolation without compilation. Ideally the fleshed out feature would give you fine grained control over type checker boundaries up to explicit manual definition (think "donut scope" and "globs") in combination with some settings/keywords as presets for well known setups like node packages and npm or pnpm monorepos. The goal is to have as much control over the exits as is given for entries (the latter has --project, files, include, exclude and other options, the former has none).

  2. What shortcomings exist with current approaches?

    tsc --emitDeclarationOnly and tsc --noEmit enforce a chronological development hierarchy from dependency to dependent, rather than nonlinear development from interface to implementation across packages. This is not scalable, blocks development and hurts DX, especially in monorepos.

  3. What workarounds are you using in the meantime?

    Not using tsc for type checking at all. Only use it for type stripping and/or declaration file generation, which will perform type checks as a side effect unless disabled.

    After some initial discussion: Also tsc --build with references mirroring package.json dependencies and devDependencies further minimises full blown builds, at the expense of manual maintenance of error free mirrors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions