0.4.10
Highlights
- Faster programs:
- A function literal written where a function with more parameters or other parameter types is expected is lowered once with that slot's parameter list instead of being wrapped in a signature adapter: a loop that calls a two-parameter callback 12 million times takes 154 ms instead of 1,544 ms.
- Typed-array conversion between integer kinds stores each element directly instead of routing it through a double:
set()of 131,072 elements from aUint8Arrayinto aUint16Arraytakes 1.9 ms instead of 61.2 ms, and thetyped-array-convertworkload runs at 0.65x Node's time instead of 1.29x. - Loop-bound versioning covers loops that write record fields, so
advanceinn-bodydrops its double counter and its double-to-integer round trip per index: scale 1 under--layouts=4 --runs=16takes 132.5 ms instead of 158.4 ms. new Error(message),Error(message)andsuper(message)into a builtin error base use the typed constructor when the message is a native string orundefined: a throw-and-catchnew Error("too big")executable is 96,760 bytes instead of 307,392, and 774 of the 779 changed corpus executables are smaller.- A same-shape clone snapshots its source's own keys only when the source keeps keys the plain copy would not reproduce: a tree rewriter over
Exprvalues built by literals runs in 311 ms instead of 3,769 ms once the program also parses oneExprand prints it. - A boxed function's name lookup sorts the module's closure-name table once and binary-searches it: with 5,178 named functions, 600,000 boxes take 166 ms instead of 1,349 ms.
- A regular expression's character class is collected, sorted and merged into the class once: six patterns of two properties and 60 ranges each compile in 1.19 ms instead of 7.20 ms.
- A start of a program with Node islands no longer parses the manifest or reads and hashes the island module tree: a warm
--versionof pi takes 560 ms and 157.7 MB instead of 1,050 ms and 171.6 MB, and makes 14,000 system calls instead of 232,000. - A regular expression anchored at the start is searched with one attempt when every path through the pattern meets a non-multiline
^first, a non-ASCII subject is matched on the bytes its string holds instead of being converted to UTF-16, ands.slice(k)on a non-ASCII string no longer reads the whole string to find its end: a tokenizer over non-ASCII text takes 12.9 ms instead of 4,876 ms against Node's 90.3 ms, a loop over a 600,000-character text takes 0.30 s instead of 381 s, a slice of one takes 10.3 µs instead of 59.1 µs, and a regular-expression call on a new non-ASCII string takes 0.04 µs instead of 64 µs at 100,000 characters. The 260 programs that link the regular-expression unit grow by 832 to 18,880 bytes. x instanceof C,new C()andC.memberwithCan untyped value, and a computed read on an untyped value, index a table from eight class values, sixty-four for computed reads, instead of walking every class of the program: a 240-class program takes 696 ms instead of 2,183 ms.- A list
Object.valuesmakes over a checked-dynamic object stays a native list throughmap,filter,flatMap,reduceRightandfindLastIndex, and the callbacks are called directly: thecatalog-tablesworkload takes 95.0 ms instead of 186.7 ms. - A record that is one arm of a union stays the same object in a checked conversion, so a write through the union value reaches the record: collecting a 300-record table takes 0.223 ms instead of 37.6 ms, and the
stream-buffered-drainworkload takes 44.7 ms instead of 59.8 ms. - A loop over rows of numbers or numeric class instances runs the rows as masked lanes, each lane running its own inner loop with the instance methods inlined: the
value-objectsmandelbrot workload takes 0.7571 of its former wall time, and every other runtime workload's generated code is unchanged. - A loop's carried values are packed only when the pack gains more than it costs, so a break-even pack stays scalar instead of adding insertion and extraction to the loop's carried dependency chain: a Mandelbrot sampling loop whose periodicity test is
|dx| + |dy| < epsruns 17% faster with the same output, and the runtime benchmark set is neutral. - A program's start-up code is emitted first, ahead of everything else in the image, and its symbols and the runtime functions they call are ordered first at link time: a program that uses fx-ts reports 186 to 191 minor faults instead of 227 to 230, touches 16 text windows instead of 37, keeps 229 to 234 resident text pages instead of 538, peaks at 3.0 to 3.1 MB instead of 4.2 to 4.5 MB and takes 0.877 to 0.886 of its former wall time.
- A program with island packages it does not use starts in 2.2 ms instead of 75.1 ms, one that uses them starts in 71.6 ms instead of 87.7 ms and cold in 60.5 ms instead of 76.0 ms, for executables 1.15% larger.
Math.random()draws from a per-thread xorshift128+ generator seeded once per thread from the system's secure source instead of asking the secure source for every number: 5,000,000 calls take 20 ms instead of 184 ms, a call takes 3.4 ns instead of 36 ns against Node's 11 ns, and themisc:math-randomworkload takes 3.0 ms instead of 36 ms.node:cryptoand WebCrypto keep the secure source for every call.- A captured binding that is filled once before a closure over it is created and that nothing rebinds afterwards is captured by value instead of through a heap box, and a binding whose every capture is of that kind has no box: the compiler's own emitted module has 10,422 boxed captured bindings instead of 43,472,
cyclic-graphsruns 12.3% faster, and the compiler's own executable is 106,875,664 bytes instead of 108,383,576. - A typed call of a method that an untyped write somewhere could replace is the declared call, direct or virtual, while the receiver has no own dynamic property and no prototype object of its class family exists: a loop of one-line method calls takes 4.3 to 4.4 ns per call and 58 instructions instead of 93.7 to 102.4 ns and 1,215, and its executable is 545,432 bytes instead of 572,528.
- Faster builds:
- A comment or whitespace edit reuses the cached frontend only when every recorded text read answers the same: a comment appended to a typed CLI that imports commander rebuilds in 0.372 s instead of 8.53 s. A build with a literal
require.resolveis cached, so the compiler's unchanged rebuild takes 11.8 s instead of 1,143.6 s, andSCRIPTC_TIMING=1reports why a build is not cached. - A build whose
tsconfig.jsonlists the output's directory restores the executable from the cache, so the second and third builds take 0.39 s instead of 1.33 to 1.38 s. - A call chain of 12 links lowers in 37 lowerings and 1.2 s instead of 24,584 lowerings and 3.7 s, and a 30-link chain builds in 1.5 s where it previously exceeded 60 s at 19 links.
--emit-lowered <file>writes the lowered program a build produced, and--from-lowered <file>builds from it without starting a type checker or lowering anything: the compiler's own source builds in 311.7 s instead of 770.8 s, and a changed source, option or compiler build is refused by name withSC3002.- The canonical-type walk maps each object node once per depth instead of once per path, and the type mapper answers each type once per depth:
scriptc coverageon json-schema-to-typescript finishes in 113 s at 2.0 GB where the previous build was stopped at 420 s, and pi's first lowering fixpoint takes 9.4 s instead of 147 to 153 s. - Lowering drops lifted functions the module no longer reaches: about 12,600 of the 54,700 functions in the self-hosted compiler's module had no caller, the phases after lowering take 8% less, and its executable is 41,520 bytes smaller.
- A run of more than 64 consecutive literal items that differ only in numbers, strings and booleans is built from columns in the program image: a 20,000-row record table builds in 15.5 s instead of 1,114 s with a 1,465 KB executable instead of 7,519 KB.
- A lowering pass that a later pass replaces ends where it asks for the next stream mode instead of lowering the rest of the program: the compiler's own lowering falls from a mean 656 s to 471 s, and its self-hosted build is 19.26% faster. A class that extends
Arraykeeps itsarrayFieldsandarraySpeciesfunctions on the pending flag, so 20 corpus programs that emitted twice now emit once. - Lowering interns each finished function's long string literals, so one string is held per failure-message text: the compiler compiling its own source has a largest heap of 4,105 MB instead of 6,424 MB and builds 2.7% faster.
- A typed program with a generator records the ambient receiver only when the module reads a generator's
this, which removes a dynamic receiver read, 3 KB and 15 symbols from every such program: 111 of the 239 changed executables in a corpus build shrank, up to 4,920 bytes. - An npm-static build no longer computes diagnostics nothing reads, so its frontend loads fall by 19 to 56 s in five of six measured pairs (median 31 s).
- A build sends the LLVM codegen helper's
version,emitandmerge-profilescommands to a resident server over a Unix socket and falls back to a process per build when no server answers; cold native builds of the Benchmarks Game programs run 3.9% to 8.8% faster, with executables identical to those of a build without the server. - Ending a stream program's push-down pass once both of its stream flags are set shortens a build of
packages/coding-agent/src/cli.tswith 15--npm-staticpackages from 1070.3 s to 882.5 s, 17.5% faster. - Nursery bypass statistics are kept per closure body, in a per-thread table keyed by the closure's function, instead of one family each for closures and for boxes holding closures, so a program's long-lived closures no longer keep its short-lived closure cycles in the nursery: a cold build of the TypeScript compiler's sources is about 2% faster and compiler peak memory 17% to 18% lower, and the runtime benchmarks are unchanged.
- The runtime pack builds
scr_events.conce per flavor instead of 16 times, and the four runtime units that reach Node islands build once per flavor and worker mode instead of having an island build of their own: the linux-x64-gnu pack falls from 241.59 MB to 233.90 MB and holds 155.2 MiB of object bytes instead of 183.5 MiB, the wasm32-wasi pack falls from 57.40 MB to 55.57 MB, and builds beyond the default executable flavor fall from 56 to 49, while executables that link the merged units grow by up to 9 KB and no program's IR or LLVM changes. - A program whose async-iteration protocol types hold a value that is not a compiled generator decides those types from the program's flows before lowering, so it lowers once instead of twice and only the slots over the implementor's element type become dynamic: a program with an
AsyncIterable<string>implementor is 724,856 bytes instead of 778,608. - A class's dynamic key tables are keyed by class and operation instead of one copy per dispatch helper, so helpers that differ only in the key's form or in
optionalshare a table: pi's first lowering round builds 4,968 tables instead of 8,073 and 796.1 MB of serialized IR instead of 910.0 MB, paired pi builds take 1,596.5 s instead of 1,643.8 s, and the compiler's own executable is 100,789,952 bytes instead of 101,760,800. Computed access on class instances through an untyped value is up to 2.7% slower and typed code is unchanged. - An expression's operands are lowered once for the same request and the value reused while that expression is lowered, so
Object.keys(Object.keys(…))nested nine levels takes 21 lowerings instead of 349,533, and two emitter passes walk the module once instead of once per item: the compiler's own build is 9.8% faster and its build of its own source 1.25% faster, with emitted code unchanged. - A typed program that reads a closed record through
Object.keys,values,entries,for...in,in,JSON.stringify, string conversion or inspection links no dynamic object model, so a program that enumerates a record of records falls from 397,184 bytes and 37 dynamic-tier symbols to 124,256 bytes and none, and an ELF partition object gives each function and datum a section so the link drops a promoted definition nothing refers to: 1,646 of 5,402 corpus programs are smaller, and the compiler's own executable falls from 103,560,232 to 101,104,024 bytes. - The compiler's own build is 7.18% faster:
pause()on a child's stream compiles, so the build makes one lowering pass where it otherwise aborts a pass and lowers again. - A rebuild after a comment-only edit is answered from the frontend cache, including in a program that declares an enum another file can name, where a rebuild of a 241-file fixture takes 1.7 s instead of 146.5 s, because import elision decides from the syntax tree and the type checker alone and a file that loses no import reads no text.
- Frontend and whole-program work that repeated per file, per import declaration or per pass is done once per load or per lowering round: import elision is decided once per project load, so the frontend load of a 400-module program takes 2.93 s instead of 3.49 s and of a 1,200-module program 7.56 s instead of 9.94 s; three whole-program answers are computed once per round, so lowering takes 25% to 43% less and the compiler holds 2.2 to 2.9 GB less on a large program; the type-only import check reuses the type-check gate's diagnostics; and the variadic check returns without walking a program that cannot need it, taking 21 ms instead of 1,150 ms on the compiler's own source.
- A type mapping walks each type it reaches once instead of once for every path that reaches it, so a chain of records that each name the next one in two members no longer costs 2^depth: a graph reduced from a real library's types builds in 2.2 s at 20 levels and 2.7 s at 40, against 92.1 s at 20.
- A mapping that passes 500,000 steps ends the build with
SC2016at the declaration of the type it was mapping; record graphs deeper than the depth limit that ran for minutes or without end now end the build in 3.8 s to 6.9 s. --coverage=reachedskips the unreached remainder pass for a large program, and the report states that the unreached code was not analyzed.
- A comment or whitespace edit reuses the cached frontend only when every recorded text read answers the same: a comment appended to a typed CLI that imports commander rebuilds in 0.372 s instead of 8.53 s. A build with a literal
- Node.js parity:
node:seais a supported builtin module under thenode:prefix only:isSea()isfalse, andgetAsset(),getRawAsset(),getAssetAsBlob()andgetAssetKeys()throw Node'sERR_NOT_IN_SINGLE_EXECUTABLE_APPLICATIONwhen their arguments do not fit.- In a JavaScript source, a call to
path.join,path.resolve, a synchronousfsfunction or a timer function with an argument of the wrong type throws Node'sERR_INVALID_ARG_TYPEwith Node's message instead of a codelessTypeError, and a timer whose callback is not a function throws it synchronously, after every argument expression evaluated. - A child launch whose file, command, argument,
cwdor environment key or value holds a NUL, or whose file is empty, throws Node'sERR_INVALID_ARG_VALUEfrom the call before a process starts, acrossspawn,spawnSync,exec,execFile, the synchronous and promisified forms, andfork. - Node's error shapes apply to a relative
require()that names no file (MODULE_NOT_FOUND), a failed spawn's'error'event (errno,syscall,pathandspawnargs, one object for every listener) and a failed directory read, whose message and error carry no path. http.request,http.get,https.requestandhttps.getcheck the names and values of theheadersoption and the derivedHostheader before a socket is taken, throwingERR_INVALID_HTTP_TOKENorERR_INVALID_CHARas Node does, and aContent-LengthorTransfer-Encodingfield in the trailers of a chunked request is refused.- A TLS client refuses a server's finite-field Diffie-Hellman group under 2048 bits, as Node does: the compiled client agrees with Node on connect-or-refuse for all 29 public TLS endpoints tested, against 28 of 29 before.
- Named imports of the
processdata membersargv,execArgv,execPath,platform,arch,pid,versionsandallowedNodeEnvironmentFlagsread the same value asprocess.<member>, and a JavaScript read of a builtin data member with no lowering is refused by name withSC2020. spawnandspawnSyncaccept a union-typedstdioslot,spawnSync'sresult.stdoutandresult.stderrwithout an encoding are branded Buffers that answer the same object on every read,Object.entries,keysandvaluesenumerate a union of record shapes, andReflect.getPrototypeOflowers likeObject.getPrototypeOf.fs.promises.writeFile(path, data, { mode, flag })andfs.appendFileSync(path, text, { encoding })lower with the creation mode, the exclusive flag and text,Uint8ArrayorBufferpayloads, sofs.promises.writeFile(path, bytes)truncates instead of appending.fs.Dirimplementsread(callback),close(callback),Symbol.disposeandSymbol.asyncDispose,fs.opendirhas its callback form,Direntanswers all seven entry-type probes, and a compiled directory walk runs 15 ms against Node's 152 ms for 200 walks of 200 entries.process.execPath,process.env,os.tmpdir()andos.userInfo().usernameread and write through the Win32 W APIs, so non-ASCII text survives a non-UTF-8 ANSI code page such as CP932 or CP1251 andforkfrom an executable in a non-ASCII path works.- The ES2025 Set methods
union,intersection,difference,symmetricDifference,isSubsetOf,isSupersetOfandisDisjointFromfollow the set-like protocol on dynamic Sets,Set.prototypeanswers brand checks andSymbol.toStringTag, andnew Set(iterable)calls a patchedaddfor each value: Test262'stest/built-ins/Set/passes 563 tests instead of 341. in,Object.keys,hasOwnPropertyandtypeofover a builtin module namespace answer Node 24's member census for both the CommonJS exports object and animport * asnamespace, andObject.keys(namespace)lists names forwarded throughexport * fromin Node's code-unit order; 500,000 mixedinandhasOwnPropertyprobes take 12.2 ms against Node's 87.2 ms.- In
node:events, symbol event names key the listener registry by identity, so twoSymbol("s")names are different events andSymbol.fornames meet across call sites, and theMaxListenersExceededWarninga compiled program reports carries Node'semitter,typeandcountfor a compiled emitter and for one built through an untyped constructor.eventNames()keeps the_eventskey order, the warning printsSymbol(description), and a replacedprocess.emitWarningreceives the warning object as its only argument. util.inspect,console.dirandutil.formatWithOptionsacceptnumericSeparatoras a literal or a run-time option and group a number's digits in threes, the integer part from the right and the fraction from the left, with a bigint's digits before its trailingnand the sign outside, at every depth and inside arrays, objects,Mapkeys and values,Setentries, typed arrays, Buffers and custom inspect options, for%s,%d,%i,%fand%o. Short integers, exponent forms,NaN,Infinityand-0are unchanged, and a non-boolean value is refused at compile time and at run time.writeFileSyncandappendFileSyncaccept the documented option records (encoding,flagfrom Node's fullstringToFlagstable, and creationmode) for string,BufferandUint8Arraydata with Node's validation order and error shapes, andfs.promises.writeFile(path, bytes)applies the creation mode and the exclusive flag, so it truncates instead of appending.fs.existsSyncaccepts a Buffer path, a union of strings and Buffers, andany-typed values, answering false with Node's one-time DEP0187 report for anything that is not a path.await import("node:<builtin>")lowers to the builtin namespace token behind the import's microtask hop for every supported module, with member calls dispatching on the awaited namespace and a destructuring pattern going through the static namespace alias handling, and a require-typednode:pathbinding answerspath.parseandpath.formatthrough the runtime's checked forms, including a value the compiler cannot see and a record held through a checked value.worker_threads.Workerimplements Node'senvoption, with object entries coerced byString(),nullandundefinedcopying andSHARE_ENVsharing, andSignandVerifystreams have the Writable surface Node gives them:write()appliesupdate()'s bytes,end()finishes at once andpipe()can pipe into one, while the Readable side stays absent.ReadableStream.fromsteps any iterable, and atype: "bytes"source builds a byte stream whose BYOBread(view)detaches the destination as Node does. The writer, controller, transform, reader, byte controller, request and queuing-strategy constructors build in a typed program,pipeTo,pipeThrough,TextEncoderStreamandTextDecoderStreamanswer as Node does,key in handlereads a class handle's getters, andnode:stream/consumerscollects a Node Readable, a webReadableStreamor any async iterable into text, JSON, a buffer, an ArrayBuffer, a Blob or bytes.- In a JavaScript source, a function value answers the
callerandargumentsreflection questions:f.hasOwnProperty("caller")is false, andf.callerandf.argumentsthrow Node'sTypeErrorfor a strict-mode function. The operators coerce their operands through ToPrimitive and ToNumeric, so"6" / 2,true - null,s << "1"and unary-,+and~follow Node, own-key probes over lib-declared builtin functions answerlengthandname, and an array-literal hole in a tuple position lowers as the position'sundefined. - Computed
import()specifiers are bounded at build time from literals, no-substitution templates,constand never-reassignedletbindings,+and template-span products capped at 256 values,?:,??,||and the parameters of module-local functions that are only ever called, and an unbounded specifier is refused at build time with the source that stopped the analysis. In an ES module,requirethrowsReferenceError: require is not definedandtypeof requireis"undefined", as in Node 24.22, andfs.accessandfs.truncateread through the boundednode:fs/promisesnamespace box as function values. server.listenreads its arguments as Node does: anundefinedornullport is an unused port, anundefinedhost is every interface and anundefinedcallback is no callback, a union withundefinedis decided at run time, and astring | numberport goes through the run-time port check.JSON.stringifyof an untyped value that Node maps toundefinedanswersundefined, andnew RegExp("/").sourceis\/.- A container that holds itself through an
unknownslot prints as Node prints it, so an untyped array that contains itself prints<ref *1> [ 1, [Circular *1] ], throughconsole.log,util.inspectwithdepth: 0andutil.formatwith%oand%O.in,hasOwnPropertyandObject.keysover a builtin namespace answer from the namespace the program loaded instead of a census generated for one platform, sonode:constantsreports a member that comes from the platform's C library, such asO_SYMLINK, andprocess.featuresanswers booleans that describe the compiled binary. Object.definePropertiesreads a descriptor map held as a typed record, a class instance or an array, defines symbol-keyed descriptors, accepts a class instance held as an untyped value as a receiver, and throws Node'sCannot convert undefined or null to objectfor a map that isnullorundefined: Test262'sbuilt-ins/Object/definePropertiespasses 742 of 1,264 variants instead of 726.- A child's
stdoutandstderr'data'listeners run in the order Node delivers them,'spawn'and'exit'events fire in spawn order, a failed spawn's'error'fires before its streams''end', and the'close'events of children whose pipes end in their exit turn follow every'exit': of 25 measured shapes, 20 match Node in 50 of 50 runs instead of 8. A promise resolved with a value of unknown type adopts it, in the executor ofnew Promise,Promise.withResolvers(), the valueutil.promisifyhands on,Promise.resolve(p)for aptypedanyorunknown, and an async generator'syieldandreturn, andpromise.finally(() => cleanup())waits for the promise the callback returns. node:net,node:http,node:dgram,node:streamandnode:cryptoreport Node's error codes and system error fields.node:http2matches Node's events and state, sosession.pinganswerstruefor a PING that left,falsepast ten unanswered pings and nothing while connecting or afterclose(). AnExpectrequest's head is sent at construction, aClientRequestemits'information',on("connect")on a request held as an untyped value installs Node's tunnel listener,agent: falseis Node's throwaway agent, and'unpipe'and'pause'are emitted through a pipe's lifecycle as Node emits them.maxBufferis enforced in child_process and the pipes close when a capture is killed,pause(),resume(),isPaused()andreadableFlowinglower on the child stream handle, a computedmaxBufferis evaluated in source order with the other launch options, and a paused output stream of anexecorexecFilechild holds 65 MB where it held 766 MB. Fatal error exit,NODE_NO_WARNINGS, port validation andprocess.disconnectfollow Node, and five Node value reads that compiled programs got wrong now answer as Node does, among themin globalThis,execArgv,typeof exportsand the zlib namespace.structuredCloneof an error gives a new Error of the class Node picks, with the source's ownmessage, itsstack, a clone of itscauseand nothing else, and a DOMException clones to a DOMException.module.enableCompileCache()answers{ status: DISABLED, message }, creates no directory and writes nothing, withgetCompileCacheDir()undefinedandconstants.compileCacheStatusavailable, and chalk 4.1.0 compiles natively, with named styles never starting Node and the color models starting it at first use.- An empty array literal takes its element type from its context, so
const [name = "none", count = 0] = table["missing"] ?? [],cond ? xs : []and a nullableany[]beside a typed arm compile where they were refused or threw at a later push. Promise.allandPromise.allSettledtake values, promise-or-value entries and tuples,Promise.allSettledgains its tuple form, and the order of turns against neighbouringthenchains matches Node. A cast of an unknown value toundefinedorvoidchecks the value and throws on anything other thanundefined,a ?? bover a left side that can only be nullish answersb, andinstanceof URLSearchParamsandinstanceof Bufferare tested at run time.- A function is one value with its own properties:
fn.call = f,Object.defineProperty(fn, "bind", …)anddelete fn.applycreate, define and delete own properties as in Node, and a function merged with a namespace or amock.fn()keeps its members, soObject.keys(fmt)and"digits" in fmtanswer as Node does. An untyped call reaches the function behind a typed slot instead of throwing before it starts. Intl.NumberFormat,Intl.DateTimeFormatandtoLocale*Stringwith options compile over ICU 78 for the default locale,"en-US"and"en": options are read when the program runs, an invalid option throws Node's error, and other locales and formatter members stay refused by name.- A built-in global called or read through the global object (
globalThis.parseInt,global.Number,new globalThis.URL, the timers,queueMicrotask) compiles to the built-in, a program that writes one of those members on the global object is refused by name, and acreateRequireloader can be stored, passed, returned and used as a shorthand member, with a request that does not resolve throwing Node'sMODULE_NOT_FOUNDwhere it is called. - A stream held in a variable writes through the stored stream as Node does, the built-in undici gains
ProxyAgentand a globalfetchwhose init carries a dispatcher goes through the package's fetch, while an object that is no dispatcher of the package is refused by name, and the built-in undici compiles beside Node types that declare neither the web stream classes norDOMExceptionas globals. writable.end(chunk)takes astring | Bufferchunk and a chunk that may be null or undefined, andspawnSync,execFileSyncandexecSyncevaluate a call or getter given aswindowsHide(andexecSync'sshell) where the member sits, in Node's order.fs.createReadStream,fs.createWriteStream, the stream constructors and aFileHandle'screateReadStream,createWriteStreamandreadLinesapply asignaloption as Node 24 does: an abort destroys the stream with anAbortErrorwhosecodeisABORT_ERRand whosecauseis the reason, emits'error'then'close', and closes the descriptor, and an already aborted signal destroys the stream when it is created.assertstrict, loose and partial deep comparisons read an accessor through its getter and compare enumerable own properties only, and the failure message shows accessors as Node's inspection does; a run-time%sruns a record's owntoString, and a string conversion of an untyped value that is an instance of a class declaringtoStringis refused withSC2020where it produced[object Object].- A workers program releases what its emitted conversion and call helpers made before returning on a thread that was told to stop (
worker.terminate(),process.exit()in a worker, shutdown): a string leaked in up to 26 of 30 runs and leaks in 0 of 100. - A function value prints by its own name and call kind wherever it is held (in an array, a record, a class field or a typed slot, and as a static method, a bound method or a method of a JavaScript class): an async function prints as
[AsyncFunction: f], a generator as[GeneratorFunction: f]and an async generator as[AsyncGeneratorFunction: f]where the inspector printed[Function: f].??=,||=and&&=on a member of a Node island object compile in JavaScript's order, and testing an island value as a condition, under!,typeofor strict equality with a primitive needs no conversion. - A class that extends an island class may declare optional parameters on its constructor and its methods and answers its constructor's
length, and a function passed by name as a child's'exit'or'close'listener may declare the code asnumberand stays findable byremoveListener; a methodlengththat cannot be read and astringsignal parameter are refused by name. - A
child_processlaunch gives the child the signal state a Node child starts with: signals 1 to 31 are at their default action with an empty mask, sochild.kill("SIGINT")reaches a child of a program started with SIGINT ignored or undernohup, andSIGCHLDreturns to default so exit codes are reported. A child'sref()andunref()answerundefined, and a socket's'close'listener is called withhadError. - A stream destroyed with a reason that is not an
Errorkeeps that value: a falsy reason destroys the stream without an error, a reason given to an untypedwrite,final,flushortransformcallback stays that value, and a value thrown by the source ofReadable.from, an operator function or a pipeline stage is the reason once rather than anAggregateError. An'error'listener whose parameter admits the value receives it, a listener that declares anErrorfails its checked conversion by name, and an untyped emitter with no listener throwsERR_UNHANDLED_ERRORfor a primitive. - Type arguments on a base class the runtime provides are erased, and extending a built-in class that has no subclass layout (
Map,Set,Promise,URL,AbortController, the web streams) is refused with one error that names the class and states what compiles instead, with or without type arguments. - A promise used as a
WeakMaporWeakSetkey is found by its identity, and a thrown promise is still a promise where it is caught (x instanceof Promise,x.then(...)); a function read through a parameter, a field, an element, aMapor a reassigned binding reports the name of its definition instead of the name of the binding it was read through. - A project that lists
domin the tsconfiglibcan name a DOM type or global even from a module imported only for a type: the name is admitted as a type and absent as a value, so a read throws aReferenceErrorandtypeofanswers"undefined", while a global Node 24 defines is never admitted this way;Object.entriesandObject.valuesover a record with run-time keys answer every value to a callback parameter or afor...ofbinding where a value that does not fit the literal's own members ended the run with aTypeError. http.Serverlowersoff,removeListener,listenersandnewListenerfor the request-side event lists, and the fallback types declareuncaughtExceptionanduncaughtExceptionMonitor.
- TypeScript parity:
consoleand its output methods are usable as values in both type worlds:const { log } = console,console.log.bind(console), a method in an object literal or a class field and aMapof sinks compile,console.log === console.logholds, and a stored method formats throughutil.format.x.constructoron an instance of a program class answers the instance's own class, so.name, identity comparisons, static reads andnew (x.constructor as T)()compile, and static accessors of a TypeScript class collect as ordinary module functions.delete rec.k, where the record's type does not declarekand the record can keep a source, removes the key from that kept source, soin,hasOwn,Object.keys,JSON.stringify, spread and the untyped read stop seeing it, and"k" in instanceon a class receiver answers from the class declaration.promise.catch(handler)accepts a handler that is not an inline function expression, including a declared function, aconstarrow, a field or method, a call result,console.logor an executor'sreject, while an absent handler is ignored so the rejection passes on.- A JavaScript file that reads an undeclared name another library declares, such as
documentorwindow, compiles and throws Node'sReferenceErrorwhen the read runs instead of being refused, and a dotted write of a property the static type does not declare lowers: 370 of the 692 Test262 variants that metassignment to non-variables are not supported yetnow pass. - A
for…ofloop over a union of array types runs the array walk with the length and the read dispatched on the tag, an async function value called with a possibly missing argument keeps its promise as its result, and a call that chooses between a function value and a loader'sresolveresolves the request at build time: the pi build reports 63 diagnostics instead of 66. - Non-Unicode regex literals whose decimal escape names no group or sits in a character class (
/\1/,/(a)\2/,/\8/,/[\1]/) compile with Node's legacy octal and identity meaning, and@param {T} [name]on a JavaScript setter reads as documentation rather than an optional parameter. AsyncIterable<T>,AsyncIterableIterator<T>andAsyncIterator<T>as value types hold the checked-dynamic value, sofor awaitsteps a compiled async generator natively and awaits a synchronous iterable one value at a time: the pi build log falls from 96 errors in 56 files to 90 in 51.- A class field typed by a type parameter (
T,T | null,T[], parameter properties, inherited base fields) compiles when the instantiation binds the parameter tounknownorobject, and a declaration-only package's config type with method or callback members maps structurally: the pi coverage run reports 1275 diagnostics instead of 1288, 13 cleared and 0 added. - The library
ArrayBufferViewinterface maps to the checked-dynamic value by its type wherever a value of it flows, and itsbuffer,byteOffsetandbyteLengthreads lower from the typed array or DataView behind it, withbufferkeeping Node's ArrayBuffer identity. Number.isFinite,Number.isNaN,Number.isIntegerandNumber.isSafeIntegerof a value that is never a number evaluate the argument and answerfalse, andnew Function(...parts)evaluates each argument, converts it to a string and throws Node'sEvalErrorwhere Node does.- An object literal that flows into a checked-identity view holds the members its declared type declares, so a
current: undefinedmember can take a value later anditems: []can take an element: 193 literal shapes match Node, against 144 matching on main. An optional chain the checker narrows as a whole compiles as the narrowed result, and++and--on abigint | undefinedslot narrowed to bigint throw the catchableTypeErrorforundefined. - An absent optional checked-dynamic field keeps its absent state through a width copy and the hybrid key walk, and a
.filter(predicate)that narrows a record to a wider type converts through the checked cast, which throws a catchableTypeErrorfor a value that does not carry the wider members. - An untyped receiver whose value is a compiled function boxes into the checked-dynamic tree, so
apply,callandbinddispatch on the runtime kind, and a generic function's capture scan skips identifiers in member-name positions. - An annotated
constorletinitialized from an existing record keeps that record's layout, so both names stay one object; the conversion becomes a copy only when a declared field cannot hold what the value stores, and a write through such a binding, a written parameter or an annotated literal is refused withSC2021instead of losing the write. - A value with extra members, an index signature, a class instance or
process.stdoutbinds to a narrower record type without a copy and reads and writes through either name,.concatand spread over arrays of records with different field sets keep each element's own layout,new Map([...])keeps its seeded entries' value arms,typeofguards on the process credential readers narrow on every target, and a project is checked with thelibits tsconfig names, the target's default library when it names none, and the declaration filesincludeadds, matchingtsc -p. Reflect.getOwnPropertyDescriptor,has,deleteProperty,get,set,ownKeys,isExtensibleandpreventExtensionsread as function values with Node'snameandlength, over the same argument-list lowering the direct call uses, withget/setreceiver substitution and Reflect's target-first check.process.stdoutandprocess.stderrflow into a record type of methods such asinterface Sink { write(s: string): void }, calling the stream's current method as Node does (200,000 writes through the record take 44.0 ms against Node's 91.9 ms), andPromise.allresults holdundefinedonly where an entry is a hole or anundefinedvalue, sorows = await Promise.all(rows.map(load))no longer widens every array of the row type.- A conditional spread whose two arms both carry keys compiles and builds the taken arm's record, a call to an override that takes more arguments than the inherited slot compiles when no class below overrides the method again, a member receiver the checker types by a generic constraint reads through the stored union,
slot as Extract<Slot, { type: T }>maps to the member the bound literal selects, a value enters a union with a catch-all arm or a shared discriminant by the arm its discriminant value names, and a record besideundefinedasserted to a subtype is checked into it. - A TypeScript
enumused as a value has a run-time object:Object.values,keysandentries,for (k in E),JSON.stringify(E), passingEto typed, generic, untyped and island code, andE[n]with a run-time number compile and match Node, while an enum read only through its members compiles as before. - A
{} as Tassertion that later writes fill in lowers to a partial record view whenT's values may be frozen, including a type a generic deep freeze makes freezable, and in a program that starts worker threads:Object.freezeof a view freezes the record it views,Object.isFrozenanswers for that record through every reference, and a write to a frozen object throws Node'sTypeError. An asserted type that is one arm of a union, can be part of a reference cycle, is published, or keeps several property orders is still refused at the assertion withSC3001. - A member call on a typed record, dictionary, tuple or array passes its holder as the receiver in every spelling (
x.f(),x["f"](),x[k](),x[i](), optional, asserted and parenthesized calls), so a function that readsthissees the object it was called on, while calling the value by itself passes no receiver: of 592 probe programs, 192 answered wrong before the change and none do now. - A read that TypeScript types
undefinedfrom an assignment in another function keeps its declared type after a call that may have undone that narrowing, so"abcd".slice(start)printscdandmustache@4.2.0renders its templates instead of the empty string, and a missing array element passed to a generic method, a static method, a private member or a field holding a generic function arrives asundefinedinstead of taking the checker's type parameter. - A static member read through a class name, through
thisin a static method or throughsuperresolves at the nearest class that declares the name, whatever its kind: a getter redeclared over an inherited method is called, and a func-typed field over a getter, a getter over a method and a getter with no inherited method of the name resolve to their own declaration. - Chained array exports, a destructured
require()of a rebuilt export object, destructuring into untyped members, and an empty array,MaporSetpassed through a generic call that takes its element type from the destination all compile. - A TypeScript file whose nearest
tsconfig.jsondoes not setverbatimModuleSyntaxis compiled with import elision, so an import whose bindings the file never uses as values, a clause oftypespecifiers only, an empty clause and a re-export of a type create no run-time edge, while a file with notsconfig.jsonabove it, a file undernode_modulesand a project that setsverbatimModuleSyntaxkeep Node's type-stripping rule. The project's own declarations join the program, and the order of class field initializers followstargetanduseDefineForClassFields, so a field initializer may read a parameter property. typeRootsreach the type checker, a module with no declarations innode_modulesis looked up under the type roots, and in a project that compiles under node10 a specifier in a declaration file or in an import Node erases may name a file the package'sexportsmap does not list, while a value import of a file the map does not list is refused as before.- Array reads over a union of tuple types from an
as consttable run in place: a method runs on the arm the value holds,for...ofwalks the value,xs[i]evaluates the receiver then the index and reads the position,.lengthis the arm's own length, and a spread keeps the conversion, while a method a tuple cannot run in place, a method that writes its receiver and a read in place where the binding is also handed to a mutable array slot are refused by name. - A class that declares
[key: string]: anyor[key: string]: unknowncompiles, with the signature declaring no storage and a name the class does not declare becoming an own property of the instance, and a catch binding narrowed to anything but a record, a union or an object type is the checked-dynamic value that anyunknownvalue takes, sotypeofon the binding answers all eight kinds. Assigning a declared field through a computed key checks the value against the field's type, and assignments to a catch binding stay refused. - A class method that is a generator with its own type parameters compiles, sync and async, instance, static and private, and a generic generator method reached through an interface runs; a generic async generator method and a field holding a generic generator function expression keep their refusals.
- A type derived from a built-in handle class through an interface,
Omit,Pick,Partial,Required,Readonly, an alias or an intersection holds the built-in object itself, with members read by name and checked against their declared type, while a required member the derivation adds throws aTypeErrorat the checked read and an instance of a subclass ofEventEmitter,Erroror a stream class passed where a record type names fields the program's own classes declare converts to a view of the instance. const { isTTY, columns } = process.stdout, the same over an alias or a parameter of the stream type, and an object pattern in a parameter list are the member reads of the stream, in TypeScript and in JavaScript. In a file where an import is elided, values a compiled package with declarations produces are converted to their declared types at the boundary as in every other file, and a generic function that fills a string-keyed dictionary key by key and returns it through an assertion to a mapped type of its type parameters compiles when the dictionary is aconstof that function that nothing else can hold.+and+=take their type from both operands together, so"m" + iis a string andthis.x += dxstays a number, and a short array pattern's positional element carriesundefinedunless nothing shrinks the literal.- A call that selects one of several declared overloads of a package compiled from its JavaScript is checked against the result of the signature it selected, and where the declaration is wrong for the input the call throws a
TypeErrorthat names the package, the declaration with its file and line, and the command that patches the declaration. - A member read keyed by a template literal reads through the fields the key's text can spell, a generic instance's pattern names bind to the record the call passed, and a
constkey that is not a literal over same-typed members is refused (SC1090,SC2004) instead of building and printingundefined. - JavaScript of a package called with typed values (numbers, Dates, Sets, Maps, RegExps, bigints, URLs, records with methods) compiles its uses of the argument's members, and a write to a member the package's JavaScript never builds is refused by name, with
--node-island=<package>as the way out. JSON.stringifyof a wide JSON-like union withbigintorsymbolarms takes the run-time traversal, and a union of scalars converts by value, so programs that were refused now build and match Node.- A class with a member whose signature has no mapping is no longer refused as a whole: its other members, its construction,
instanceofand its subclasses compile, and a use of the refused member is refused at that use by name. An override may declare a narrower parameter or fewer parameters than the member it overrides, an argument past the last parameter is still evaluated, a value the narrower override does not accept throws aTypeErrorbefore the body, and a method value'slengthis that of its own declaration. - An
Errorsubclass that keepscodeitself as a field, an index signature or adeclarefield reads the value it writes,.findand.findLastwith a written type predicate run the callback as a test and convert the accepted element with a check, a predicate that accepts an element it cannot convert throws aTypeErrorat run time, andforEach,reduceandreduceRightwork on tuples. - A position read over a union of tuples joins the arms into a checked-dynamic value where that value holds every other answer itself:
const [missing, found] = await page()withpage(): Promise<[Error] | [null, Response]>compiles, and a handle beside a record, an array, a promise or a class instance at one position stays refused with the same diagnostic.
- CommonJS and islands:
- A marked TypeScript module runs in a Node island with its types stripped, and a mark the build cannot honor stops it with
SC8006: the entry module, a TypeScript module Node cannot strip, a path or glob that matches no module of the program, or a package the program does not load fromnode_modules. - A function exported by a Node island package can be used as a value:
versions.sort(rcompare),forEach(note),{ compare },new Set<F>([f])and timers reach it, and every view of one function is one function to===,indexOf,includes,SetandMapkeys. - Awaiting an island promise that is an instance of a
Promisesubclass whose constructor does not take an executor resolves it through the subclass's ownthen, soawait client.messages.create(…)completes instead of rejecting withTypeError: Promise resolve or reject function is not callable. - Island values answer as Node does where they differed: a comparison to
undefinedon a missing key follows the island's value, spread andObject.assigncopy the own enumerable keys,instanceofandObject.prototype.toString.callreport the class or tag, andstructuredCloneclones an island object instead of throwingDataCloneError; the crossing matrix matches Node in 331 of its 1,030 cells instead of 292. - A workspace package whose bare import resolves to its TypeScript source compiles its JavaScript modules with it instead of fencing their bindings with SC1090, and the issue's program runs natively in 3.3 ms against Node's 67 ms.
- A CommonJS module whose whole export is one value answers that value to a plain
require()binding, an ES default import and acreateRequirebinding:module.exports = Object.getOwnPropertyDescriptorgives a binding that is the function itself, with Node'snameandlength, and a binding exports the value it held at the export statement. --definefixes aprocess.envvalue at build time (--define='process.env.NODE_ENV="production"'):ifstatements the value decides are resolved before type checking, so only the taken branch compiles and modules that only the other branch requires never join the program. Conditions built from defined reads, string literals,===,!==,==,!=,!, truthiness and&&/||whose left side decides the result are resolved, includingelse ifchains, nestedifs,switchcases and several defines at once, and the executable sets the variable when it starts, so the program, its child processes and its worker threads see the build's value.--definevalues must be JSON strings withprocess.env.NAMEkeys, and defining the same variable twice, assigning or deleting a defined variable, and--lib,cache warmandmerge-profileswith the flag are errors.- The
@scriptc/libnode-<platform>packs publish as opt-in packages:npm i @scriptc/libnode-<platform>installs the Node.js library a Node island loads, whilescriptcand@scriptc/compilerdepend on none of them, andSC8005names the file, the@scriptc/nodeAPI that reached into Node, and the host's install line. Linux packs carry their own C++ runtime, and a pack that still references aGLIBCXX_orCXXABI_version is refused when it is built. - A checked conversion accepts an island handle: a record reads each field through the handle, an array, tuple or index-signature record walks a native view released at every exit, and
JSON.stringify,Object.keysand object spread and rest read an island object's own properties in Node, sotoJSON, accessors and function-valued members keep their meaning. A value crossing whose type comes from an@typespackage or from a payload union of copyable and native shapes crosses as a handle at the type the program observes, so ws, express and hapi compile. - A refused package that only a Node island imports stays inside the island that loads it instead of getting an island of its own, so mocha, hapi, express, koa and the apps whose islands pulled in refused helpers compile. A dotted
node_modules/.prismadirectory resolves as a package directory, a CJS barrel's export expression is a dependency edge, surplus arguments to a JavaScript-implemented callee evaluate and go unused, and a method call a class does not declare is a checked-dynamic read and call. - Compiling one package no longer changes how another package is typed: packages that are not compiled are read as they are, so adding a compiled package leaves a neighboring island package's typing and the program's diagnostics unchanged.
- A program record nested in a record type that receives an island value becomes a live view whose members read from the island at any depth through unions, intersections, optional members, promises and nested literals, while a member declared as an array, tuple,
Map,Set,Date,RegExp,ErrororURLis refused at the crossing withSC8010naming the member path and declared type. A native I/O handle passed into a Node island is refused at build time withSC8010where it crosses, alone, as an arm of a union or as a member of a literal:ChildProcess,FileHandle, the http, net, http2 and dgram handles,Dir,SpawnSyncReturns,HashandHmacare runtime handles the island export table does not take. - A native
AbortSignalorAbortControllerthat reaches a Node island crosses as a twin, a real object of the island's own class:abortedandreasonagree in every listener of either side, an abort from either side runs both listener lists, the program's signal comes back unchanged when the twin crosses out, and a signal the island made is adopted natively, sofetch,AbortSignal.any,timers/promises,events.onceand a listener'ssignaloption accept it. - A native
WeakMap,WeakSetorFinalizationRegistrykeyed by an object a Node island made keeps its entries inside the island keyed by the island object, so two handles for one object are one key and an entry is found after the program drops its handle while the island keeps the object.FinalizationRegistry#registerand#unregistertake island targets and tokens, and a value that is not an island value is held in a native cell that crosses by reference, somap.get(key) === valueholds. --node-islandreads a bare value as a package name, so a package named like a file (highlight.js,chart.js,three.js) can be marked by name, by<pkg>/or by a subpath, while./x.js, an absolute path and a glob mark the program's own modules, and--node-island=<pkg>/subtogether with--native=<pkg>is refused.--native=<name>and--npm-static=<name>are held against the packages the program loads, so a name the program does not load isSC8006with a hint, andscriptc coverageno longer lists an unloaded--npm-staticname as compiled.- A Node island's modules are probed in the order the program evaluates them: loads that share a file answer for each other's reads and effects, a module-level constant whose initializer is closed over an island package is evaluated inside the island, a program module that runs in an island defers when its top level does nothing, and resolution is answered from a load plan computed at build time with a deferred refusal reported on stderr the first time it fires. On 50 small programs about the order and set of loads, 21 answer wrongly before and none after.
- A TypeScript module marked
--node-islandthat binds a type withouttypeis refused withSC8006for a default import and forexport { X },export { X } fromandexport default Xof an interface, naming the binding and thetypeform to write. - Island code sees
process.argvandprocess.stdout.write, theuncaughtException,unhandledRejection,rejectionHandledandwarningregistries andprocess.exitCodeare one value across native code and a Node island, so a replacement installed by either side is seen by both, and a native error keeps its identity, class and fields through island frames. A thenable is awaited as Node awaits it, an island promise only island code handles is not reported as unhandled, and an unreferenced island timer does not fire. - A build-time warning,
SC6007"divergence: island value crosses as a copy", reports where a checked conversion copies an object or array a Node island made, and the cross-heap collector releases a record or class instance without a cycle header that an island was handed, which stayed a root for the rest of the run: 90,000 records handed to an island left 90,000 exports held, and two major collections freed none. 3,681 dynamic programs become 256 to 744 bytes smaller by testing the island table once and calling it. - Typed arrays, Buffers, DataViews, ArrayBuffers and symbols keep one identity across the Node island boundary: a program view that goes into an island and comes back is itself, island code sees one object for one program view, and a
unique symbolhanded to island code is the same symbol there, which removes theSC8010refusal.Buffer.isBufferof a Uint8Array an island made answers false, as in Node, and a resizable or detached program ArrayBuffer, and a view of one, are refused by name at the crossing. - A JavaScript constructor that returns an object of another kind answers it for a construction whose result is an untyped value, and
Object.defineProperty(module, 'exports', { get })in a package becomes the whole export of the getter's value, with the getter run as often and in the same order as Node's loader.fn.constructor = valueon a function is an own property of it, and arequire()inside a function of a compiled package that names a package the program does not compile runs that package in a Node island the call loads. - A worker thread that calls into a Node island works: each island operation is posted to the thread that started the island and the worker waits for the answer, so a synchronous island call from a worker runs where the program ended in V8 with
Fatal error in HandleScope::HandleScope. A worker can hold island objects, functions and errors, while a native function, object, promise or error, an island promise, a typed array,Buffer,ArrayBufferor a symbol crossing between threads is refused by name withERR_SCRIPTC_ISLAND_WORKER_VALUE. - A value that came from a Node island keeps the island's object under the type the program reads it through: an assertion or a declared type on an island value no longer makes a native copy, and a name bound to an island export,
thisin a native subclass of an island class and a parameter handed to an island keep their origin, withSC6007reported where a view cannot hold the type. - A union arm that fits several arms of the destination converts by its own discriminant and is refused by name (
SC2003) where another value can still reach it,Headerssupports iteration, a binding that a native module imports from an island package and exports again stays an island binding, andObject.hasOwnon an island object answers as Node does. - A UMD package and a package that only a literal
import()reaches run in a Node island under the automatic policy, and a CommonJS function export keeps its own name. clearTimeout,clearIntervalandclearImmediateof an island's timer call Node's own function, so clearing an interval started in an island lets the process exit; in a program with islands the program's timer ids start at 2^52 so a number names one timer whichever side created it, an immediate takes its id from the timer sequence soclearTimeouton an immediate no longer cancels a timeout with the same number, andprocess.chdirgoes through the island unit so island code resolves relative paths against the current directory.- A child process an island started can be held at the
ChildProcesstype: listeners register on the island's emitter, properties, methods and streams are the island object's, adoption happens at the checked conversion, and a listener that an earlier listener removes during an emit is still called in that emit.
- A marked TypeScript module runs in a Node island with its types stripped, and a mark the build cannot honor stops it with
- Fixes:
- An
http.getresponse with status 101 switches protocols only when it carries anUpgradeheader andConnection: upgrade; any other 101 is an ordinary final response, and an upgrade with no'upgrade'listener closes the request instead of never settling. Promise.allover promises of nothing fulfills with a real array holding oneundefinedper entry wherever the array can be read, and a missing array element passed to a function declared inside another function arrives asundefinedinstead of throwingundefined is not representable in the target union.- An uncaught
Error, and an unhandled rejection whose reason is anError, print the stack frames the Error captured, oneatline carrying the function name and file, as Node prints its stack trace. - Four lowering bugs that emitted ill-typed IR, each ending as an
SC9001internal compiler error on ordinary TypeScript, are fixed, and the final module of a 31,070-function program goes from 17 validator errors in 14 functions to 0. - An
intest on a value that still has a union type answers from the value when no arm declares the key, and an instance of a program class that extendsErrorkeeps its identity, class and fields when it is boxed from a union arm, sokey in viewandReflect.hasreach the error's ownstackandcause. - A record argument is bound at its own layout when the callee writes through a narrower parameter or compares it by identity, so the callee's writes reach the caller's object as under Node, and
+=on a string member of a checked-dynamic value lowers instead of reportingSC1043. typeofof a value called through a%callslot answers"function"instead of"object", sotypeof chalk,typeof chalk.greenand a union narrowed bytypeof x === "function"match Node.- A recursive function or union type reached again through its own function arm ends the checked-dynamic conversion walk instead of overflowing the stack: compiling
yaml3.0.0-2 with eight casts for library declarations finishes in 11.7 s at 589 MB. new Proxy(target, handler)is refused at compile time withSC1090when the handler arrives as typed storage, or the target is a class instance or an array, which were refused only once the statement ran.- A value that checker flow narrows to
neveris lowered as the live dynamic value, so a read no longer throwsTypeError: expected number, andswitch (null)and array or object destructuring fromnullorundefinedfollow Node's behavior instead of failing with an internal compiler error. - An LLVM backend refusal reports the statement and the function it was raised for, with the refused type, instead of the entry file's first byte.
- A child handle wrapper is requested from a program's
childProcessmodule fact rather than a text scan, so a corpus program's binary drops from 693,800 to 112,720 bytes. - Each shard object names its compilation units after itself, so a Darwin debug map tells one object's symbols from another's, and the island's pthread record names Node's stack for the life of each job, so
process.report.getReport()no longer kills a program on macOS. - A build with
--optimization devreports the file, line and column of every stack frame, names the class members of a frame and prints the position for module-level code, for 16 bytes of every call and a 3.8% higher geomean over 57 benchmark workloads. - The executable cache keys a
--tier=static-onlybuild, soscriptc build --tier=static-onlyandrunreport SC2003 when the default build of the same file is cached, as a cold build does. - A comparison with
undefinedornullthat folds to a literal still evaluates its other operand: calls,awaits, getter reads, assignments, constructions and sequence expressions run once before the literal,(f(), undefined) === undefinedis true, andObject.is(f(), undefined)runs the call. A missing array element at a slot the instance cannot widen throws theTypeErrorthat slot throws instead of answering false. scriptc buildwith--optimization=speedprints one warning per runtime pack built without the host LLVM codegen helper, naming the missingspeedflavor and the release objects it links instead, and a dev build withSCRIPTC_CONVERSION_TRACE=1reports each failed checked conversion on stderr with the conversion's file, line, column and error, whether or not the program catches it.- A two-argument call whose callee resolves through
constaliases toObject.defineProperty,Reflect.definePropertyor eitherdefinePropertiescounts as a possible child addition, soconst define = Object.defineProperty; define(child, …)no longer throws aTypeErrorat run time, and a keyed record write whose receiver can beundefinedaccepts anullorundefinedvalue instead of failing with an internal compiler error. - An async function whose declared result is a concrete type adopts a returned checked-dynamic value that is a promise at run time: the caller receives the settled value converted to the result type, a rejection rejects the function's promise, and a value that is not a promise converts immediately.
- Every runtime refusal for an operation that is not implemented carries a scriptc diagnostic code, so the 111 that named none print with the nearest compile-time code, and
DataView.prototype'sbyteLengthandbyteOffsetaccessors throw Node'sTypeErrorfor a view over a detached buffer or one that shrank below it instead of answering 0. - A write or
deletethrough(value as View),!,satisfiesor<T>addresses the operand's object as Node does for dotted writes, dotted updates with+=or++,deleteand a keyed write whose literal key names a member the operand's layout holds at the asserted type, while every other keyed write, update or short-circuit assignment through a converting assertion is refused by name withSC1090.(p as any).k = vwrites a slot the record declares, and a write through an assertion to a record besideundefined, which was written to a converted copy, is refused withSC1090. - A
letor field read before its assignment readsundefinedas Node does instead of0,falseor a fault: an unsettled binding takesT | undefinedstorage, a field takes the checked deferred-init slot,??and string conversion see the undefined slot, and a binding or field that is provably assigned first compiles as before. scriptc coveragelists every checked conversion whose value is typed by something no build can verify, undertrusts unverified typesand astypeClaimsin--print=diagnostics, one row per declaration, package, parsed-data type or untyped binding with the sites that trust it, and each declaration row prints thescriptc types patchcommand that fixes it;--fail-on=type-claimsfails on them.- Every executable starts its small-object heap at a different address in every run, glibc executables bind their imports at load and keep the table read-only, and musl release executables carry no debug payload: a musl hello is 86,632 bytes instead of 1,835,384 bytes.
- A record converted from a checked object keeps a declared property non-enumerable when the object held it that way, so
Object.keys,values,entries,for...inandJSON.stringifyleave it out as Node does, and a package boundary accepts a non-enumerable data property instead of refusing it. - A caught island error is read on the Node stack and released once the program drops it: a loop of 30,000 caught island errors prints the same counts in five runs of five instead of exiting 133 in four of five, and 100,000 caught errors hold 104,768 KiB with 49 island objects at exit.
- Five wrong results of partial record views are fixed: an empty
{} as Tthat is dropped no longer leaves its keys on the next view allocated at the same address, a write to a record-typed property the literal did not write keeps its value, a record or array stored in such a property is kept by reference, and a heap use after free inscr_record_view_extrasis fixed. - In a program with a partial assertion, reading a required property that nothing wrote throws a catchable
TypeErrorthat names the property for a computed-key read,??=and+=on a keyed property, the(groups[key] ||= []).push(x)idiom and a spread, where these forms ended the process with exit 134. Reflect.set(instance, { toString() { … } }, v)runs the key'stoStringonce, a builtin function used as a value keeps one identity through every container and reports Node'snameandlength, and six meaning-preserving rewrites of 318 corpus programs that produced 17 internal compiler errors now compile and match Node or are refused by name with a source position.Readable.fromandDuplex.fromover an instance whose[Symbol.asyncIterator](),async *[Symbol.asyncIterator]()or[Symbol.iterator]()comes back through an untyped member build and match Node instead of reportingSC9001, and a program that holds anode:http2session or stream type and calls nothing fromnode:http2ornode:httplinks, where it failed withSC3004and undefined references.- A union that flows whole into one of its own record arms converts each arm the checker still sees at the site, so an async function that returns
promise.then((r) => ({ ...r, extra }))answers the callback's record instead of throwing a representabilityTypeError: of 455 cells, 394 match Node's output and exit code instead of 283. An arm with no conversion is reported where the flow is written with the diagnostic that arm's plain flow into the record gets, and a typed object with a callablethenthat is not a promise is refused withSC1090. - A build answered from the executable cache reports every warning the original build reported, and the launcher answers only the options a command takes: a cached
--fail-on=blockers,--range,--dry-run,--wasm,--explain-dynamic=x, an unknown--tieror--windows-subsystemon another target now reports the full CLI's usage error instead of printing the path and exiting 0, andscriptc coverage main.ts --tier=static-only --fail-on=blockersno longer replays the default tier's verdict. - A class method read as a value checks its receiver whenever its source mentions
this, so a call throughconst f = logger.errorwith no receiver throws aTypeError, and(c.m)(),c.m!()and(c.m as F)()call the method onc.Object.create(...)where a record type is expected is refused withSC1090, a program that holdsprocessas a value is not compiled in workers mode unless its source names a worker API orprocess.getBuiltinModule, and"code" in errorfor an Error subclass that declarescodeanswers true. - A file-scope JavaScript binding whose initializer is a computed read of
globalThis[KEY], a literal of plain data beside a checked arm of a conditional or logical expression, or an assignment into an untyped member is stored in its module and read by separately declared functions, including through an object shorthand and an export entry: in a family of 26 programs, 23 match Node against 16 before. A file that uses its ownimport typebinding as a value and also has an import that elision drops reportsSC0001at the use. - An internal compiler error or a checker panic in the body reports
SC9001at the innermost expression being lowered and the build goes on with the rest of the program, a panic in a speculative whole-program analysis givesanyfor the node instead of ending the build withSC0004at line 1,x instanceof Cwherex's type is a union with a class the compiler rejected is refused by name at the test, and a rest parameter typed as a fixed tuple and only forwarded into a method that some code replaces on an instance is built from its slots at the read. - The build-time load recorder reads only the arguments whose answer the engine gives without running program code: an options object that is not a proxy and holds no accessor, locales that are a string, an
Intl.Localeor a plain array of those, the receiver and compared value of the String methods as primitives, and the parts and name ofnew File()as strings or buffers.crypto.createECDH,crypto.getDiffieHellman,crypto.createSign, the ECDH, DiffieHellman, DiffieHellmanGroup and Sign classes, and theasync_hooksasync id counters are not treated as functions that compute from their arguments, so a package that generates a key pair when it loads gets a fresh key in each run. - A spread of a record that kept its source object into an index-signature record copies every own key in the object's order, as Node does, and a build that stops at packages it can neither compile nor run in a Node island reports the program's own errors in the same attempt, with each refused package's hint naming the setting that would run it in Node.
- A reference read out of a freshly made value keeps that value's group out of an arena, so
new Slot(rows()).held,rows()[1]and the same read through a call's arena no longer read released memory in fully typed code, and the memory asplice, afillor a push into an arena list held is released. - A compound assignment in a function specialized for a typed record argument keeps its operands in a release build: a record field is no longer stored as
NaN, a right-hand-side call is no longer dropped, and a string+=no longer ends with SIGSEGV when a class also implements the parameter's interface. The specialized clone is made only where its operands evaluate in the same order and with the same effect as the original sequence. - A class that implements an interface with an optional member it leaves out is held in a union as the interface, so an instance of another class of that interface keeps its identity and its methods run with the real instance as receiver, and a class method read as a value answers its own name.
- A function that a natively compiled package declares inside a function is a new object each time the outer function runs, so two instances are no longer
===and a property set on the second no longer replaces the first. - A union type that reaches itself (
type Json = string | Json[],type Nest = Nest[] | null, or two aliases that hold each other) compiles where the compiler ended withRangeError: Maximum call stack size exceededand no diagnostic. - A promise that settles while a collection runs no longer reads freed memory: the settle detaches its callback list before it runs the first waiter and frees the list at the end, so a program that holds several dynamic views of a typed promise and drops one before the promise settles matches Node where a release build exited 139 and a sanitized build reported a heap use after free. Executables that link the promise runtime grow by at most 0.05%.
- An element or key read with
??or||takes a result type that holds both sides, sonames[at] ?? nullanswers the default when the read is absent instead of throwing thatnullis not representable in astringslot, and a default of a wider type is no longer converted into the left side's type. Where only a copy could place the default in the element's layout, a missing element stops with aTypeErrorthat names the default and the build reports the site once (SC6001), and passing a class instance that has a base class to a record parameter no longer reportsSC6001. - A module that re-exports a
const enum, or a namespace of const enums, keeps the export a value export in import elision, for a specifier that names a local binding and for one that names another module's, and forexport default, so a program that imports the re-exported name and uses a member as a value compiles. The names kept as values are reported in aninlinedExportsfield besideinlined. - A value that a callee builds out of the members of a freshly made list no longer outlives the list's arena: a holder that keeps two of the group's members stays valid, so a sanitized build no longer reports a heap use after free under
scr_arena_dropand a plain build no longer writes into reused memory. - Executables link vendored zlib, brotli, zstd and mbedtls built with bounds traps, stack canaries and fortified libc calls, and the runtime units are built with zero-initialized small locals and stack-clash probes, so a read of an uninitialized small C local yields zero. Every executable grows, from 0.02% to 3.04% (median 0.33%), and run time is unchanged.
- An