Deno 1.24 separates TypeScript checking from JavaScript emission
Deno 1.24 uses swc for all JavaScript emission and stops emitting during deno check. Separate caches let successful type checking and generated output be reused on their own terms.
Checking a TypeScript program and generating its JavaScript output are different operations, even when one command performs both. Deno 1.24 makes that distinction explicit in its implementation. It uses swc for emission regardless of whether type checking is requested, and deno check no longer emits JavaScript.
The July 21 release notes explain the previous split. With the check flag enabled, Deno used the TypeScript compiler to convert code. Without checking, it used swc. The new arrangement removes that dependency between the checking choice and the emitter.
I value the tools around TypeScript more than the language's added complexity. A runtime that includes checking should make the cost and responsibility of that operation clear. This release interests me because it changes that architecture without asking users to abandon type checking to obtain the faster emitter.
Checking no longer implies emission
The emission and checking code at the release tag separates configuration for bundling, checking and single-module emission. Its checking configuration includes noEmit: true. The comments explicitly describe checking as independent of bundling or emission through swc.
That is a substantive boundary. The type checker answers questions about the module graph and its declarations. The emitter transforms source into executable JavaScript. A successful check does not need to generate an output artifact merely to prove the types are acceptable.
The separation also avoids making the checking flag choose between two emit implementations. A program can use checking while receiving output from the same transformation path used elsewhere. The release notes identify swc as the faster emitter, but the architectural benefit extends beyond a particular timing result.
For a check-only command, the removed work is direct. There is no reason to populate emitted JavaScript merely because a developer wants diagnostics. A later execution can request whatever output it needs and use the emission cache for that purpose.
This also makes command timing easier to interpret. A faster deno check can reflect avoided emission as well as reuse of previous checking results. It would be inaccurate to attribute every improvement to a faster TypeScript inference algorithm. The runtime has changed which work the command performs.
Output reuse depends on source and options
The emission function first computes a source hash using the source text and the emission-options hash. It asks the emission cache for code matching that identity. When suitable output exists, it returns that code. Otherwise it transpiles the parsed source and writes the result into the cache.
This is the point at which cache identity becomes part of correctness. Output depends on both the program text and the transformation configuration. Reusing an artifact solely because the module name has not changed would ignore inputs that can change the generated JavaScript.
The source's function signature keeps the module specifier separate from the hash. The cache needs to identify the module and determine whether its stored result matches the current transformation inputs. These are related operations, but a stable module address does not imply stable content.
The release notes describe a more robust emitted-code cache. The tagged implementation provides a concrete explanation for reuse in this path. It can return existing output when the current source and options match, while leaving type-check result reuse to another cache.
Successful checks have their own identity
The checking path computes a hash for the relevant module-graph segment. If it finds a known successful check and reloading is not requested, it returns without invoking the TypeScript checker again. The implementation therefore distinguishes a previous successful result from merely having encountered the source before.
The hash includes the checking mode and TypeScript configuration. It also iterates module entries in a deterministic order and incorporates relevant specifiers and source text. This gives the cache a representation of the inputs whose checking result it is trying to reuse.
Different media types participate according to whether the checker needs them. The implementation includes TypeScript sources and declarations, while JavaScript inclusion depends on the checking configuration or the file's check marker. A graph with no files requiring a check can return without performing one.
The cache records success after processing diagnostics. It does not simply mark every attempted check as completed successfully. This matters because caching a failed check as a success would turn a repeated command into false reassurance.
The code also retains TypeScript build information for reuse. When reloading is requested, it avoids the previous build information. The result is more than one cache layer serving different purposes, with a high-level successful-check decision and lower-level compiler information available when another check is needed.
I prefer that explicit model to a single cache whose contents are assumed to mean that every preparation step is complete. Generated JavaScript and successful diagnostics are different facts. They should each be invalidated by the inputs that determine them.
Reported improvements describe specific cases
The release notes use the Oak repository as an example. They report faster first-time checking and larger reductions on later runs, with especially large improvements when Deno recognizes that checking already succeeded. Those results illustrate the difference between avoiding work and performing necessary work faster.
They are not a guarantee that every repository receives the same percentage improvement. A changed graph may require another check, and a cold output cache may still require emission. The practical mechanism is identifiable even when a project's timings differ from the example.
The release also improves editor behavior around dependency caching. Its notes say some scenarios no longer require restarting the language server after caching dependencies. That fits the broader requirement that tools notice when the state they rely on changes, although it is a separate user-facing improvement from emission.
Deno 1.24 leaves the typed development workflow intact while giving its stages separate implementation paths. A check-only request can stop after diagnostics, a known successful check can be reused, and emission can return JavaScript prepared from matching source and options. Those distinctions explain where repeated work disappears without confusing the presence of executable output with evidence that types have been checked.