Repository navigation
Conversation
… field order The declaration files a tsconfig's files and include name, and the packages its types names, join the program: a project's own global declarations are part of its type world. A file whose tsconfig compiles class fields in the constructor (target below ES2022, or useDefineForClassFields: false) assigns its parameter properties before its field initializers run, as tsc's output does, so an initializer may read one. The tsconfig is the nearest one above each file. Settings that keep one meaning are named once instead of surfacing as TypeScript errors: legacy decorators and the CommonJS-only import and export forms report SC0016, and a tsconfig that is silent about strict says so in SC0002. importsNotUsedAsValues and preserveValueImports, which tsgo's config parser drops, are read from the tsconfig's own text. Co-Authored-By: Claude Code <noreply@anthropic.com>
… entry A project that sets module to commonjs types a package through its require entry, where export = can merge a namespace of types into the value. The TypeScript files are ES modules for Node and for scriptc, which resolves the import entry; 'Cannot find namespace' on such an import now says so. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-Authored-By: Claude Code <noreply@anthropic.com>
…es its modules A project's own declaration files now join the program, and a project that types an untyped package itself (`declare module "pkg/lib/core.js"`) then types an island package's imports from a file the island does not own. The boundary called a type the island's only when every declaration of it was in the package's directory or its @types twin, so the module's namespace became a native type (`import("pkg/…").then` was refused) and the default export was refused at every use. An augmentation of a package's interface broke the same way, also when it is written in a program module. A `declare module "<name>"` block declares the module it names: when that is a module of an island package, its declarations are the island's, wherever the block is written (the project's .d.ts, a typeRoots package, a program module, another package's types). The namespace, the interfaces declared in the block, and an interface the block augments are island types. An island object whose type has no native representation and that the island does not declare (a global interface of the project's file, named by its block) stays a handle where the program only uses it through island operations, as arrays, Maps and Sets the island does not declare already do. Any other use is still refused by name. Co-Authored-By: Claude Code <noreply@anthropic.com>
In a file whose tsconfig compiles class fields in the constructor, the parameter properties are assigned before the field initializers run, so `"id" in this` in an initializer is true for a parameter property `id`. The test still answered false, the answer for ES2022 class fields, where the parameter properties are assigned after every initializer. Co-Authored-By: Claude Code <noreply@anthropic.com>
… value matrix `v.constructor.name` on an island object was refused (SC2020): the read's type, Function, has no native representation, so the lowering did not treat `v.constructor` as an island value and looked for a native `Function.name`. An island object of such a type now stays a handle under island-only use, so the read is an island operation and answers as Node does. The 29 entries `…ctor: refused:SC2020` of eight island-class, island-subclass and island-error-class cells are gone, plain and sanitized; nothing is added. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
This PR regressed programs that use an npm package as a Node island; fixed and pinned on this branch (42b444d, f448878). The branch also has main 6cc6787 merged in (through #3171's branch, so the diff against its base is unchanged), and the review thread about What regressed. Not packages typed by their own declarations (bundled, or // types/paint-kit.d.ts, named by the tsconfig's "include"
interface PaintKit { has(name: string): boolean; list(): string[] }
declare module "paint-kit/lib/core.js" { const kit: PaintKit; export default kit; }
declare module "paint-kit/lib/index.js" { const kit: PaintKit; export default kit; }import kit from "paint-kit/lib/core.js"; // paint-kit runs in a Node island
kit.has("bold"); // SC8010 on 05c509c46, at every use of `kit`
void import("paint-kit/lib/index.js").then((mod) => mod.default.has("soft")); // SC2020 on `.then`Main compiles this (the import is An augmentation of an island package's interface ( The fix.
A side effect of the second rule: Pinned (each differential against Node with the package forced into an island, plain and sanitized):
Ran on this head. Zero-cost gate with |
With `typeRoots` configured, tsc resolves a `types` name under each root as a file or a directory, as it does a module: `types: ["measures"]` with `typeRoots: ["./typings"]` names typings/measures.d.ts before it names a typings/measures directory. The lookup here only knew the directory form (its package.json `types`/`typings`, then index.d.ts), so a project whose entry is a declaration file of its own lost those declarations: tsc accepts the project and Node runs it, and the build failed with "Cannot find name" for every global the file declares. typeRootEntry tries `<root>/<name>.d.ts` first. tests/corpus/9318-typeroots-declaration-file has one entry of each form. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Superseded by #3386 (train cli 5), which carries this PR's commits unchanged, with the comment-edit cache fix and the project-resolution change on top; it was validated there as one tree. Closing in its favour. |
…port-elision-reads The island value matrix keeps main's pins here; it is regenerated on the merged tree. Co-Authored-By: Claude Code <noreply@anthropic.com>
…ge in the tree The merge kept main's pins. With vercel-labs#3207 in the tree an island handle no longer refuses a read of its constructor, and the update mode of the matrix drops the 29 refused:SC2020 entries for those reads. Nothing else moves. Co-Authored-By: Claude Code <noreply@anthropic.com>
… no longer refused Reading `.constructor` of an island object is an island operation since the tsconfig adoption this branch carries (vercel-labs#3207); main's table, re-pinned since, still lists the 29 `.ctor` observations of eight cells as `refused:SC2020`. Regenerated for those cells on this tree (island-class.i, .u, .w, island-error-class.i, .u, island-subclass.i, .p, .u): the 29 entries go, nothing is added. Also formats resolve.test.ts. Co-Authored-By: Claude Code <noreply@anthropic.com>
…arations resolve as its tsc resolves them The branch carries vercel-labs#3171 (import elision) and vercel-labs#3207 (the project's tsconfig for ambient declarations, types and class field order) with the comment-edit cache fix of fix/import-elision-reads. Co-Authored-By: Claude Code <noreply@anthropic.com>
Stacked on #3171 (import elision); the first commit here is that PR's.
What
scriptc checks every project with its own compiler settings and takes only the strictness flags,
pathsand the JSX settings fromtsconfig.json. Of 19 open-source TypeScript projects tried against main, 9 stopped in preflight on that difference, with TypeScript errors that their own tsc does not report. This PR takes from the project what does not change what the native program means, follows one more setting in the lowering, and names the settings that keep one meaning.Taken from the project:
.d.tsfiles thatfilesandincludename (outsidenode_modules) and the packages thattypesnames (throughtypeRootswhen set) join the program. The program used to grow from the entry's imports only, so a project's global declarations were never read. An API declared there and not implemented is refused where it is used, by name, instead of failing the type check.targetbelow ES2022, oruseDefineForClassFields: false, read from the nearest tsconfig above each file). tsc then initializes fields in the constructor, after its parameter properties, soprivate size = this.items.lengthmay read the parameter propertyitems. ES2022 class fields run the initializer first and readundefined, which TypeScript reports as TS2729. The lowering now emits the parameter-property assignments first for classes of such files, and that TS2729 is not reported there; the same error about a field declared further down stays. A tsconfig that does not mentiontargetkeeps ES2022 order, and a program with no tsconfig is unchanged.importsNotUsedAsValuesandpreserveValueImportsfor the import elision of Drop the imports tsc drops in projects that do not set verbatimModuleSyntax #3171. TypeScript 5.5 removed them and tsgo's config parser drops them, so they are read from the tsconfig's own text.Named instead of followed, once per setting (
SC0016, new):experimentalDecorators): one diagnostic at the first decorator, with the count, instead of one TypeScript error per decorator (60 in class-validator). A class decorator of one parameter behaves the same under both decorator semantics and still compiles, unlessemitDecoratorMetadatais set.module: commonjs:import x = require()andexport =report oneSC0016instead of TS1202/TS1203 per site, and "Cannot find namespace" on a package's default import explains that the project reads the package'srequireentry while an ES module resolves itsimportentry.strict:SC0002now says the tsconfig "does not enable" strict when it is simply silent about it.Packages that run in a Node island (added with 42b444d):
declare module "pkg/lib/core.js" { … }in a.d.tsits tsconfig includes, or in a typings directory named bytypeRootsandtypes), or that augments an interface of a package (declare module "pkg" { interface Api { extra(): number } }), then types an island package's imports from a file the island does not own, and the boundary no longer saw island values: on 05c509c the dynamic import's.thenwas refused (SC2020) and the default export was refused at every use (SC8010). Adeclare module "<name>"block now declares the module it names: when that is a module of an island package, the block's declarations are the island's, wherever it is written. An island object typed by a global interface of the project's file (outside the block, with no native representation) stays a handle where the program only uses it through island operations, the rule arrays, Maps and Sets the island does not declare already follow; any other use of it is still refused by name.TypeError: expected … from a Node islandat run time..constructorof an island object (v.constructor.name) is an island operation instead of SC2020: 29 entries of the island value matrix (tests/harness/island-value-matrix/expected.json), all…ctor: refused:SC2020, are gone; nothing is added.libstays scriptc's own (never below ES2025, no DOM). Of TypeScript 7's ESNext slices,disposable,arrayanderrorare already loaded;collection,typedarrays,temporalanddatedescribe APIs Node 24 does not have;intlrefers to Temporal's types;sharedmemory(Atomics.pause) is the one slice Node 24 ships that is not loaded, and is left for a change that also checks the runtime. The two projects that failed "on lib" declare the missing members themselves in files their tsconfig includes, which is the first point above.Proof
8923-tsc-class-fields-target(// @tsc-emit,target: es2019): field initializers that read parameter properties directly and through a method, in a base and a derived class; the oracle is tsc's own output under Node.8924-project-ambient-declarations: global types from a.d.tsthe tsconfig includes and nothing imports.scriptc.packages, differential against Node, plain and sanitized:8930-node-island-project-ambient-module(the project's.d.tsdeclares global interfaces and exact and patterndeclare moduleblocks for an untyped CommonJS package: the shape pi has for highlight.js),8931-node-island-module-augmentation(an augmentation in a project.d.tsand one in a program module),8932-node-island-typeroots-ambient(typeRootsandtypes; interfaces declared in the blocks, held in bindings, parameters and results),8933-node-island-typeroots-attypes(@types/<name>named intypes),8935-node-island-tsconfig-imports(bundled types: default, named and namespace imports, the package's types on native values, beside a project global.d.ts),8939-node-island-tsc-elision(// @tsc-emit: Drop the imports tsc drops in projects that do not set verbatimModuleSyntax #3171's elision over island imports; an island module whose only import is unused is not loaded, as in tsc's output).8923-tsc-class-fields-targetalso pins"id" in thisin a field initializer for a parameter propertyid(true when parameter properties are assigned first; review thread).tsconfig-legacy-decorators,tsconfig-commonjs-import-equals,tsconfig-commonjs-package-namespace,tsconfig-strict-unset,tsconfig-target-field-order(the TS2729 that stays).Uint8Array.fromBase64, which Node 24 does not haveZero-cost gate
--gate-corpus=all, head 363651c (this stack with main 6cc6787 merged in) against base 6cc6787: PASS, 5246 identical, 103 fail on both sides, 2 allowed, noruntime-onlyrows; the self-hosted compiler builds with the candidate; compile time −0.58% [−2.73%, +0.34%], fixtures −0.21% [−1.50%, +1.08%], bench +0.00% [−1.30%, +1.08%], all within tolerance. The two allowed programs are #3171's (corpus/6120-type-only-import-graphas written on main,fixture/llvm-layouts), with--gate-allow='^import \{ type \w+(, type \w+)* \} from'; this PR adds none: no corpus program, benchmark or fixture on main has a tsconfig with ambient declarations, atypesentry other thannode, or a target below ES2022. The gate builds without a libnode pack, so island programs are skipped on both sides; the lanes and the island suites below cover them. An earlier round (05c509c against 960d2da, before the island change) gave 5202 identical, 96 fail on both sides, the same 2 allowed.Lanes and bootstraps
Both full lanes in Sandboxes on 363651c: plain 22,106 passed, sanitized 18,873 passed, the plain and the sanitized native bootstrap included. The only failures, 8 in each lane, were eight cells of
tests/harness/island-value-matrix.test.ts, whose table still listedv.constructor.nameon an island object as refused; f448878 removes those 29 entries (no entry added), and the affected cells pass against the committed table, plain and sanitized. With a libnode pack assembled from this branch: the eighttests/harness/node-island*.test.tssuites (105 tests) and the 91 island corpus programs pass, plain and sanitized.pnpm lint,pnpm format:check,pnpm test:ts7 --baselines-onlyand the tsgo/tsc-ts identity check for the new programs pass. Two earlier rounds of both lanes ran on this stack before the island change (first plain 21,561 and sanitized 18,334 passed, 0 failed; second 21,616 and 18,383 with seven compile timeouts on a slow pool that pass alone).pi
pi's root tsconfig sets
verbatimModuleSyntaxandtarget: ES2024, so import elision and field order do not apply to it; itsincludenames five ambient.d.tsfiles, which are now loaded. One build each on the same 32 vCPU sandbox (Intel Xeon @ 2.50GHz): main 62 errors in 1,903.8 s, this stack 62 errors in 1,839.1 s. One site changes its first diagnostic:void import("highlight.js/lib/index.js").then(…)is now typed by the project's owndeclare module, and reports SC2020 for.thenon that promise where it reported SC1090 for the dynamic import of a CommonJS module; a probe that awaits the same import is refused with the same SC1090 by both compilers. Peak RSS was 8.6 GB on main and 10.3 GB on this stack in that single pair of runs; I did not find the cause, and it is one sample.With highlight.js as a Node island (the build's own decision when the package is not in
--npm-static), pi'ssyntax-highlight.tsbuilt alone showed what the island change fixes. Main reports one unrelated error for it (SC1090, an island package's default exports as object-literal values). 05c509c added SC8010 at the use of the package's default export, which pi's ownhighlight-js.d.tstypes with a global interface (a full build of pi with that package as an island showed it at all three uses, with SC2020 on.thenof the dynamic import). On this head the file's diagnostics are main's again, and with that object literal written as arrows (the scratch edit the running pi uses) it compiles, as it does on main (same sandbox, 32 vCPU, Intel Xeon @ 2.90GHz; a 3 s build).Known differences that remain (recorded, not fixed here)
undefined, as in ES2022; tsc's output for a target below ES2022 creates no property, soObject.keysof such an instance differs from the project's own build.targetunder TypeScript 5 (default ES5) gets ES2022 order..d.tsare loaded but are not static record types; values typed by them are checked-dynamic (SC6104)..d.tsis used where the island hands it over (read, called, compared withundefined, passed back); storing it natively is SC8010 by name, main's rule for an island object typed as a program record. Declaring the type inside thedeclare moduleblock makes it the island's type, a handle everywhere.structuredCloneof an island-made object into a record type declared in the project's own.d.tsbuilds and throws aTypeErrorat run time where Node copies; the same with the type declared in a program module works. Recorded with a repro for the island conversions work (islands: a value an island made passes the checked conversions #3029).🤖 Generated with Claude Code