Skip to content

fix: declare typescript as an optional peer dependency - #4134

Merged
kamilmysliwiec merged 1 commit into
nestjs:masterfrom
lazerg:fix/typescript-peer-dependency
Sep 22, 2026
Merged

kamilmysliwiec merged 1 commit into
nestjs:masterfrom
lazerg:fix/typescript-peer-dependency

Conversation

@lazerg

@lazerg lazerg commented Sep 19, 2026

Copy link
Copy Markdown

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Issue Number: #4132

The CLI plugin imports typescript at runtime, but the package lists it only as a devDependency. Nothing tells the package manager which copy the plugin should get, so it takes whatever happens to be reachable from its install location. In a pnpm workspace with more than one TypeScript around, that can be a different version than the one @nestjs/cli compiles with.

The plugin inspects the compiler's types but compares type.flags against the TypeFlags of its own copy, and TypeScript 6 renumbered them:

5.9.3 6.0.3
String 4 32
Number 8 64
Enum 32 65536
BigInt 64 128

So with the program on 6.0.3 and the plugin on 5.9.3, a string reads as an enum and a number reads as a bigint:

static _OPENAPI_METADATA_FACTORY() {
    return { name: { required: true, enum: string }, age: { required: true, type: () => BigInt } };
}

The build succeeds and the app throws ReferenceError: string is not defined as soon as the DTO is loaded. The same source with both sides on 6.0.3 emits type: () => String and type: () => Number.

What is the new behavior?

typescript is declared as an optional peer dependency, so the package manager resolves it from the application instead of leaving it to hoisting. In a pnpm workspace the plugin now gets the application's copy linked into its own node_modules:

# before
node_modules/.pnpm/@nestjs+swagger@…/node_modules/@nestjs/swagger
  (no typescript; resolution falls back to the hoisted copy)

# after
node_modules/.pnpm/@nestjs+swagger@…/node_modules/
  typescript -> ../../typescript@6.0.3/node_modules/typescript

The range is ^5.5.0 || ^6.0.0. 5.5 is the floor because the package is ESM: on 5.4 and below import { TypeFlags } from 'typescript' fails with does not provide an export named. Marking it optional follows ts-morph in @nestjs/graphql, which is plugin-only in the same way.

@nestjs/graphql has the same problem and is reported in nestjs/graphql#4139.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Closes #4132.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI plugin imports typescript without declaring it and can load a different version than the compiler, emitting enum: string

2 participants