AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

Looking back at the week of 29 January–4 February 2025

Running TypeScript does not check its types

TypeScript 5.8 beta checked for erasable syntax, while Node stripped types without checking them. Keep execution and compiler checks separate in the project.

Citiți articolul în română

A runtime that strips TypeScript syntax answers whether it can execute the remaining JavaScript. It does not answer whether the program satisfies its declared types. Treat execution and compiler checking as separate obligations, even when removing a build step makes a small script easier to run.

Looking back from November 2026, this distinction is the useful part of the January 2025 discussion. It prevents a convenient local command from becoming accidental evidence that the application has been checked. The consequence for a business is straightforward: a simpler route to execution should not silently remove a source of feedback before release.

Read the beta as a beta

Microsoft's TypeScript 5.8 beta announcement is dated 29 January 2025. It introduces erasableSyntaxOnly, which rejects TypeScript constructs requiring runtime transformations, including enums and parameter properties. That is a historical beta feature, not a claim about stable availability that day or the defaults of a November 2026 installation.

Earlier that month, the Node.js 23.6.0 release, dated 7 January, enabled experimental type stripping by default in that release. Its “Current” label belongs to that dated release page. It does not make that old release a recommendation for a new application now.

The versioned Node 23.6 documentation explicitly separates stripping from type checking and describes a transform option for syntax that needs code generation. Versioned documentation gives this retrospective a firmer boundary than applying today's general Node documentation backward.

Erasing an annotation leaves the operation

Consider an explicitly illustrative variable declaration whose value is a number and whose annotation says it is a string. Removing the annotation leaves a JavaScript assignment that can execute. The fact that execution proceeds does not prove agreement between the value and the annotation; the annotation has been removed from that execution path.

A compiler check is the place to report that mismatch. A runtime test can still discover a later failure, but only if it exercises behavior that exposes it. These checks serve different purposes. Replacing one with the other changes what evidence a team receives, even when both commands happen to pass on a particular revision.

That distinction matters most when a script grows. A one-off operation can acquire callers, configuration and external data without anyone deliberately redesigning its release process. If “it ran once” remains the only acceptance condition, the team's confidence can outgrow its evidence. Keep the check visible while the execution path becomes simpler.

Some syntax asks for new behavior

A parameter property illustrates the second boundary. In a constructor, a declaration such as public name: string does more than describe the parameter's type: it also declares and initializes an instance property. Simply erasing the TypeScript-specific words would lose the property behavior that callers expect.

An enum also introduces a runtime representation. A code path reading a named enum member expects a value to exist during execution. Removing the declaration as though it were only a type annotation cannot preserve that expectation. This is why the beta restriction is about syntax with runtime consequences, rather than a rule that all TypeScript is interchangeable with annotated JavaScript.

One possible redesign is to write ordinary JavaScript operations explicitly and use erasable annotations around them. Another is to keep the required constructs and use a transforming compiler or runner. Neither choice is universally cheaper. Compare the clarity of the source, the surrounding project and the responsibilities of the toolchain before rewriting established code.

Give execute and check separate names

A proposed project convention should name an execute command and a check command. The execute command runs a selected entry point under the agreed runtime, for example node file.ts in that historical Node version. The check command runs the pinned TypeScript compiler against the intended project configuration without emitting application output, for example tsc --noEmit --project tsconfig.json. In the January beta context, the syntax restriction belongs in that compiler configuration where the team chooses it.

Treat this as a design proposal, not a tested configuration supplied by this article. Exact commands depend on the project's module model, supported runtime and file layout. The important evidence is that both commands operate on the intended revision and scope. An editor showing no errors is weaker evidence if its compiler or project selection differs from the release check.

When a command fails, distinguish its responsibility. An execution failure can concern data, environment or unsupported syntax. A compiler failure can concern type relationships or the selected syntax policy. Record enough context to reproduce the result instead of treating every failure as a reason to disable the newer option.

Transforming tools still have a place

The strongest counterargument is that a mature project may use constructs requiring transformation throughout its code. Preserving a maintained build pipeline can be clearer than changing source just to eliminate that pipeline. A runner that performs transformations may also fit development tasks whose requirements exceed simple stripping.

Such a runner still does not automatically establish that type checking occurred. Examine what its command actually promises. If checking is separate, preserve it explicitly. A short command name can hide several behaviors, so document the result rather than relying on the word “TypeScript” in the tool's description.

This argument is distinct from new compiler diagnostics and from rewriting import paths. It concerns two boundaries: whether syntax can be erased without losing runtime behavior, and whether a separate check validates the program's types. Other compatibility work may still be necessary, but it should not obscure those questions.

For custom software development, ask the team to show the execute path and the check path for one important entry point. Confirm the runtime and compiler versions each uses. That modest exercise makes a reduced build process reviewable without confusing fewer commands with fewer responsibilities.