TypeScript 5.9 Study Guide (for JavaScript, Node.js, and React developers)
TypeScript 5.9 Study Guide (for JavaScript, Node.js, and React developers)
Last verified: 2026-01-13 Latest stable release line shown on the TypeScript site: TypeScript 5.9 (TypeScript) Latest patch listed on GitHub releases: 5.9.3 (GitHub) TypeScript 5.9 announcement date: 2025-08-01 (Microsoft for Developers)
This document focuses on what’s new/important in TypeScript 5.9, plus the practical configuration + patterns you’ll use in Node.js and React.
Table of contents
- 1. What “latest TypeScript” means
- 2. Install and verify TypeScript 5.9
- 3. The biggest change in 5.9: a new default
tsc --init - 4. What’s new in TypeScript 5.9
- 5. Modern TS config, explained (Node + React)
- 6. Recommended
tsconfig.jsontemplates - 7. React + TypeScript patterns you’ll use constantly
- 8. TypeScript type system refresher (high leverage)
- 9. Upgrade checklist to 5.9
- 10. What’s coming next (6.0 and the native TypeScript 7 line)
- 11. Practice exercises
- 12. References
1. What “latest TypeScript” means
TypeScript has:
- A stable release line (today that’s 5.9 on the official site). (TypeScript)
- Patch releases within that line (GitHub currently lists 5.9.3 as the latest patch). (GitHub)
Also: the TypeScript team has been working on a new native toolchain (“TypeScript 7” line) and describes TypeScript 6.0 as a “bridge” release, but that’s roadmap context—not what most projects call “latest stable TypeScript” today. (Microsoft for Developers)
2. Install and verify TypeScript 5.9
Install per-project (recommended)
npm install --save-dev typescript
npx tsc -v
This is the standard workflow for Node.js projects, and using a lockfile keeps everyone on the same TS version. (TypeScript)
Global install (okay for quick experiments)
npm install -g typescript
tsc -v
The TypeScript download page explicitly calls out global installs as convenient for one-offs but recommends per-project installs for reproducibility. (TypeScript)
3. The biggest change in 5.9: a new default tsc --init
In TypeScript 5.9, running tsc --init now generates a much smaller, more prescriptive tsconfig.json by default, instead of a huge commented file. (TypeScript)
Key defaults from that generated config include (summarized):
module: "nodenext"target: "esnext"types: [](important!)sourceMap: truedeclaration: truedeclarationMap: truenoUncheckedIndexedAccess: trueexactOptionalPropertyTypes: truestrict: truejsx: "react-jsx"verbatimModuleSyntax: trueisolatedModules: truenoUncheckedSideEffectImports: truemoduleDetection: "force"skipLibCheck: true(TypeScript)
Why this matters to you (Node/React dev)
This new default pushes you toward:
- Modern JS output (high
target) (TypeScript) - Modern module semantics (
nodenext+ explicit module detection) (TypeScript) - Cleaner module emit rules (
verbatimModuleSyntax) (TypeScript) - Bundler-friendly correctness (
isolatedModules) (TypeScript) - Stricter correctness checks (like
noUncheckedIndexedAccess) (TypeScript)
But: the new default also includes settings you may want to change depending on whether you’re building an app or a library—especially declaration/declarationMap and types: []. (TypeScript)
4. What’s new in TypeScript 5.9
4.1 import defer
TypeScript 5.9 adds support for the ECMAScript deferred module evaluation proposal via:
import defer * as feature from "./some-feature.js";
Important constraints and behavior:
- Only namespace imports are allowed (
import defer * as …). (TypeScript) - The module is loaded, but not evaluated until you access an export (e.g.,
feature.someExport). (TypeScript) - TypeScript does not downlevel/transform
import defer. It’s intended for runtimes/tools that support it, and it only works under--module preserveand--module esnext. (TypeScript)
When it’s useful: deferring expensive initialization or side effects until a feature is actually used (think: optional subsystems, “only on client” code, feature flags, etc.). (TypeScript)
Compare with import() (dynamic import):
import()is asynchronous and typically loads+evaluates the module immediately when awaited.import deferis designed to delay evaluation until first use of an export (even though the module is already loaded). (TypeScript)
4.2 Stable Node 20 mode: --module node20 / --moduleResolution node20
TypeScript 5.9 introduces stable Node 20 modes:
--module node20--moduleResolution node20
The release notes describe node20 as:
- Intended to model Node.js v20
- Unlikely to pick up new behaviors over time (unlike
nodenext) (TypeScript) --module node20also implies--target es2023unless you override it (whereasnodenextimplies the floatingesnext). (TypeScript)
The module reference also notes node20 includes support for require() of ESM in the appropriate interop cases. (TypeScript)
Practical guidance:
- If your production runtime is fixed at Node 20,
node20is a good “stable anchor.” - If you want TypeScript to track the newest Node behaviors more aggressively, use
nodenext(and accept occasional behavior shifts). (TypeScript)
4.3 Better DOM API tooltips
TypeScript 5.9 includes summary descriptions for many DOM APIs (based on MDN documentation), improving the in-editor learning experience when you hover DOM types. (TypeScript)
4.4 Better editor hovers
TypeScript 5.9 previews expandable hovers (a “verbosity” control with + / -) in editors like VS Code. (TypeScript)
It also supports a configurable maximum hover length via the VS Code setting js/ts.hover.maximumLength, and increases the default hover length. (TypeScript)
4.5 Performance improvements
Two notable optimizations called out in the 5.9 notes:
- Caching intermediate instantiations during complex type substitution (helps performance and can reduce “excessive type instantiation depth” problems in heavily-generic libraries). (TypeScript)
- Faster file/directory existence checks (less allocation overhead in a hot path). (TypeScript)
4.6 Breaking-ish changes to watch for
lib.d.ts changes: ArrayBuffer vs TypedArrays (including Node Buffer)
TypeScript 5.9 changes built-in types so that ArrayBuffer is no longer a supertype of several TypedArray types, including Node’s Buffer (which is a subtype of Uint8Array). This can surface new errors around BufferSource, ArrayBufferLike, etc. (TypeScript)
Mitigations suggested in the release notes include:
- Update to the latest
@types/nodefirst. - Use a more specific underlying buffer type (e.g.,
Uint8Array<ArrayBuffer>instead ofUint8Arraydefaulting toArrayBufferLike). - Pass
typedArray.bufferwhen an API expects anArrayBuffer. (TypeScript)
Type argument inference changes
TypeScript 5.9 includes changes meant to fix “leaks” of type variables during inference. This may produce new errors in some codebases, and often the fix is to add explicit type arguments to generic calls. (TypeScript)
5. Modern TS config, explained (Node + React)
This section explains the settings you’re most likely to see (especially if you start from tsc --init in 5.9).
types: why types: [] can surprise you
- By default, TypeScript includes all “visible”
@types/*packages. - If you specify
types, TypeScript only includes what you list. - Therefore
types: []means include no global@typespackages. (TypeScript)
Common consequences:
- Node projects need
"types": ["node"]plus@types/node. (TypeScript) - React projects may need
"types": ["react", "react-dom"](or you can remove thetypessetting and let TS auto-include visible@typespackages). (TypeScript)
verbatimModuleSyntax: predictable import/export output
With verbatimModuleSyntax, TypeScript keeps imports/exports without a type modifier, and drops anything explicitly marked type. This simplifies “import elision” and makes output closer to “what you wrote.” (TypeScript)
This is especially valuable when you want consistency across:
tscemit- Babel/swc/esbuild transforms
- mixed ESM/CJS packages (TypeScript)
isolatedModules: bundler/transpiler compatibility
isolatedModules warns you about TS patterns that can’t be safely transpiled one file at a time (common with Babel/swc-style transforms). It doesn’t change runtime behavior; it’s a correctness guardrail. (TypeScript)
noUncheckedSideEffectImports: catch typos, but watch asset imports
By default, TypeScript may silently ignore unresolved side-effect imports (import "x";). With noUncheckedSideEffectImports, TypeScript errors if it can’t resolve them. (TypeScript)
If you import assets like CSS, you’ll often solve this by adding an ambient module declaration:
// src/globals.d.ts
declare module "*.css" {}
That exact workaround is recommended in the option docs. (TypeScript)
moduleDetection: "force": treat every file as a module
moduleDetection controls how TS decides whether a file is a “script” or a “module.” The "force" option treats every non-.d.ts file as a module, which avoids a lot of accidental-global-script weirdness. (TypeScript)
noUncheckedIndexedAccess and exactOptionalPropertyTypes: stricter correctness
noUncheckedIndexedAccessaddsundefinedwhen you access properties via “maybe present” keys (like index signatures). (TypeScript)exactOptionalPropertyTypestreatsprop?: Tas “either missing, or T” (not “T | undefined”), which catches subtle bugs around explicitly assigningundefined. (TypeScript)
skipLibCheck: faster builds, lower strictness for .d.ts
skipLibCheck skips type-checking of declaration files. It can speed builds and reduce friction during upgrades, at the cost of some type-system accuracy. (TypeScript)
6. Recommended tsconfig.json templates
These are starting points; adjust for your runtime and toolchain.
6.1 Node.js (Node 20, ESM)
Use stable Node 20 semantics.
{
"compilerOptions": {
"target": "es2023",
"module": "node20",
"moduleResolution": "node20",
"rootDir": "src",
"outDir": "dist",
"strict": true,
// Important if you started from TS 5.9's `tsc --init` default:
"types": ["node"],
"sourceMap": true,
// For an app you might not need these:
"declaration": false,
"declarationMap": false,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"moduleDetection": "force",
"skipLibCheck": true
},
"include": ["src"]
}
Why these choices:
node20is a stable mode intended to model Node 20 behavior. (TypeScript)types: ["node"]is needed if you otherwise havetypes: [](the 5.9tsc --initdefault). (TypeScript)verbatimModuleSyntaxandisolatedModulesare common modern defaults and are part of the new 5.9 init template. (TypeScript)
If you do want
.d.tsoutput (e.g., you’re building a library), flipdeclarationback totrue. (TypeScript)
6.2 React (bundler: Vite/Webpack/Next, etc.)
For most React apps, you often want TS to type-check only and let your bundler transpile.
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
// Bundler-friendly resolution rules
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
// Let the bundler emit JS
"noEmit": true,
// If you started from TS 5.9's init, don't forget types
// (or remove "types" entirely to use default visibility).
"types": ["react", "react-dom"],
"verbatimModuleSyntax": true,
"isolatedModules": true,
"moduleDetection": "force",
"skipLibCheck": true
},
"include": ["src"]
}
Notes:
moduleResolution: "bundler"is documented as a mode for bundler-style projects. (TypeScript)jsx: "react-jsx"is the 5.9 init default and is explained in the JSX compiler option docs. (TypeScript)noEmitmakes TypeScript skip output, which is ideal if Babel/swc/esbuild handles transpilation. (TypeScript)- If you keep
types, remember it changes which@typespackages are loaded globally. (TypeScript)
6.3 Libraries (publish types)
If you publish a package, you typically want .d.ts output:
{
"compilerOptions": {
"target": "es2020",
"module": "esnext",
"moduleResolution": "bundler",
"rootDir": "src",
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"strict": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"skipLibCheck": true
},
"include": ["src"]
}
declarationgenerates.d.tsdescribing your public API. (TypeScript)declarationMaphelps editors jump from.d.tsback to original.ts. (TypeScript)
7. React + TypeScript patterns you’ll use constantly
Strong prop typing without ceremony
type ButtonProps =
| { variant: "primary"; onClick: () => void }
| { variant: "link"; href: string };
export function Button(props: ButtonProps) {
if (props.variant === "link") {
return <a href={props.href}>Link</a>;
}
return <button onClick={props.onClick}>Primary</button>;
}
Why this rocks:
- Discriminated unions give you automatic narrowing based on
variant. - You reduce runtime checks and avoid invalid prop combos.
Typed event handlers
function SearchBox() {
const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
console.log(e.currentTarget.value);
};
return <input onChange={onChange} />;
}
useState with correct inference
const [count, setCount] = useState(0); // number inferred
const [user, setUser] = useState<User|null>(null);
Use noUncheckedSideEffectImports safely with CSS/assets
If you enable noUncheckedSideEffectImports, set up ambient modules for assets (CSS, SVG, etc.) so import "./styles.css" stays type-safe. (TypeScript)
8. TypeScript type system refresher (high leverage)
If you already write JS/React daily, these are the TS concepts that pay off fastest:
Union types and narrowing Use unions to model “states” and branches, and let TS narrow with
if,switch,in,typeof, etc.unknownoveranyunknownforces you to validate before use—ideal for API responses.Generics (with constraints) Build reusable helpers:
function first<T>(arr: readonly T[]): T | undefined { return arr[0]; }
Mapped + conditional types These power a lot of modern library types (and your own schema/type helpers).
satisfies+as constfor “validated literals” Great for config objects where you want literal types but still want validation.
9. Upgrade checklist to 5.9
- Upgrade TypeScript (project devDependency) and run a full type-check. (TypeScript)
- Watch for ArrayBuffer/TypedArray/Buffer errors caused by
lib.d.tschanges. (TypeScript)- First try updating
@types/node - Then consider using
typedArray.bufferor more specific typed array parameters (as described in the release notes) (TypeScript)
- First try updating
- If you see new generic inference failures, add explicit type arguments where needed. (TypeScript)
- If you run
tsc --initand copy that config into a React/Node project, double-check:types(becausetypes: []is restrictive) (TypeScript)- whether you really want
declaration/declarationMapin an app (TypeScript)
10. What’s coming next (6.0 and the native TypeScript 7 line)
The TypeScript team’s roadmap update (Dec 2025) states:
- TypeScript 6.0 will be the last release based on the existing TypeScript/JavaScript codebase (no 6.1 planned, though patch releases may happen). (Microsoft for Developers)
- 6.0 is described as a bridge between the 5.9 line and 7.0. (Microsoft for Developers)
- They discuss running the existing
typescriptpackage side-by-side with a native preview package and usingtsgofor faster type-checking in some workflows. (Microsoft for Developers)
This is mostly “keep an eye on it” information unless you’re chasing very large-project performance improvements.
11. Practice exercises
- Recreate the 5.9
tsc --initdefaults, then modify them for:- Node 20 backend
- React bundler frontend (TypeScript)
- Try
import defer:- Create a module with obvious side effects (
console.log, expensive initialization). - Import it with
import deferand confirm evaluation timing. (TypeScript)
- Create a module with obvious side effects (
- Fix a Buffer/ArrayBuffer typing issue:
- Write a small function that accepts
ArrayBuffer. - Pass a
Uint8Array/Buffer, and fix it via.bufferor more specific typing. (TypeScript)
- Write a small function that accepts
- Turn on
noUncheckedSideEffectImportsand keep your CSS imports working:- Add
declare module "*.css" {}in a global.d.ts. (TypeScript)
- Add
12. References
- TypeScript homepage (shows the current stable line as 5.9). (TypeScript)
- TypeScript download/install instructions (npm install, npx tsc, global install notes). (TypeScript)
- TypeScript 5.9 release notes (all new features + behavioral changes). (TypeScript)
- TypeScript 5.8 release notes (background on Node ESM/CJS interop + erasable syntax). (TypeScript)
- TSConfig reference pages used above:
verbatimModuleSyntax(TypeScript)noUncheckedSideEffectImports(TypeScript)moduleDetection(TypeScript)types(TypeScript)target(TypeScript)isolatedModules(TypeScript)skipLibCheck(TypeScript)noUncheckedIndexedAccess(TypeScript)exactOptionalPropertyTypes(TypeScript)noEmit(TypeScript)declaration(TypeScript)declarationMap(TypeScript)moduleoption details (including Node modes) (TypeScript)moduleResolution(including bundler mode) (TypeScript)jsx(TypeScript)
- GitHub releases list for the latest patch (5.9.3). (GitHub)
- Roadmap update: “Progress on TypeScript 7 – December 2025” (6.0/7.0 direction). (Microsoft for Developers)