TypeScript and strict mode
TypeScript and strict mode
TypeScript is a language that adds static types to JavaScript. This post lays out TypeScript's origins, how it works, and what strict: true actually turns on, all on a factual basis. We also touch on the gap left by runtime validation and where similar tools sit.
1. About TypeScript
The language was led by Microsoft's Anders Hejlsberg (known as the designer of Turbo Pascal, Delphi, and C#). The first public release came in October 2012, and 1.0 followed in April 2014. The repository is microsoft/TypeScript, licensed under Apache-2.0.
TypeScript is designed as a superset of JavaScript. Every valid JS is valid TS, and the compiler emits JS with type information stripped. In other words, TS types exist only at compile time and leave almost no trace at runtime (exceptions: artifacts like enum and namespace).
There are two distributables — the compiler tsc and the language server tsserver. The latter powers autocomplete and error reporting in VS Code, WebStorm, and others.
2. Structural typing
TypeScript's type system is structural typing. Unlike Java or C#'s nominal typing, two types are compatible based on shape, not name:
interface Point { x: number; y: number }
function len(p: Point) { return Math.hypot(p.x, p.y) }
const p = { x: 3, y: 4, label: "a" } // not declared as Point
len(p) // passes if the shape matches
3. What strict: true turns on
Setting "strict": true in tsconfig.json flips the following bundle of options at once (from the official Strict Mode Family doc):
| Option | Meaning |
|---|---|
noImplicitAny |
Treats inference failures that would default to implicit any as errors. |
strictNullChecks |
null and undefined are no longer automatically included in every type. Only intentional places get T | null. |
strictFunctionTypes |
Enforces strict contravariant checking on function parameters. |
strictBindCallApply |
Type-checks arguments to bind, call, and apply. |
strictPropertyInitialization |
Forces class fields to be initialized in the constructor (requires strictNullChecks). |
alwaysStrict |
Auto-prepends "use strict" to emitted JS files and analyzes in strict mode. |
useUnknownInCatchVariables |
The default type of e in try { } catch (e) is unknown instead of any. |
Among these, strictNullChecks alone is often cited as delivering the most value. It moves null/undefined runtime errors to compile time.
4. Generics, conditional, and mapped types
// generic
function first<T>(arr: T[]): T | undefined { return arr[0] }
// mapped type
type Partial<T> = { [K in keyof T]?: T[K] }
// conditional type
type NonNull<T> = T extends null | undefined ? never : T
When combined with infer, keyof, and template literal types, you end up with a small functional language operating at the type level.
5. Other paths
| Tool | Position | Note |
|---|---|---|
| TypeScript | Microsoft, 2012 | De facto standard. tsc handles checking + transpiling. |
| Flow | Meta, 2014 | A similar effort. Now mostly internal to Meta. Combined with the Hermes engine. |
JSDoc + // @ts-check |
1995 (JSDoc) | Types in plain JS files without a build tool. tsc can read it. |
| esbuild, swc, Babel | Separate | Skip type checking. They only strip type information (fast). Checking goes through tsc --noEmit separately. |
esbuild and swc deliberately do not check types — they erase them. In large codebases, it is common to split build and check: build with the fast tools, run tsc --noEmit separately for verification.
6. tsconfig starting point
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true,
"esModuleInterop": true
}
}
noUncheckedIndexedAccess and exactOptionalPropertyTypes are not part of the strict bundle, but turning them on separately is common.
7. Runtime validation and zod
Types live only at compile time. Anything coming from outside — JSON, form input, environment variables — has to be validated separately at runtime. The library that fills this slot most often:
import { z } from "zod"
const User = z.object({
id: z.string().uuid(),
email: z.string().email(),
age: z.number().int().nonnegative()
})
type User = z.infer<typeof User> // static type extracted automatically
const u = User.parse(jsonFromApi) // throws on failure
Beyond zod (Colin McDonnell, 2020), valibot, ArkType, yup, and joi sit in the same slot.
| Library | First release | Note |
|---|---|---|
| zod | 2020 | TS-first. Most widely adopted. |
| yup | 2015 | Started as form validation. Often paired with Formik. |
| joi | 2012 | Originally for the hapi server. Established in node backends. |
| valibot | 2023 | Tree-shaking friendly. Function-level imports. |
| ArkType | 2023 | Parses TS expressions directly to build runtime types. |
8. Common pitfalls
The difference between any and unknown — any bypasses checking, while unknown means "identity unknown". The old behavior where catch variables were any becomes unknown under useUnknownInCatchVariables. It cannot be used until narrowed, which is the safe default.
Overuse of as assertions — assertions are a tool that turns checking off. They tend to grow in places that could use a validation function (parse) instead.
The enum trap — TS enum produces a runtime object and is hard to tree-shake. The frequently recommended alternative is an as const object plus a union type extracted from it.
Separation of types and runtime — types disappear at build time. Runtime branches must be built with validators like zod, or with typeof and in directly.
Type definition packages — JS libraries may not ship types. @types/<pkg> (DefinitelyTyped) is a separate package.
Closing thoughts
TypeScript strict + zod is the standard pair that covers both compile time and runtime. Keep any and as deliberately scarce and the type system delivers most of its value. Making every external input pass through one zod parse becomes a habit that pays off heavily in operational stability.
Next
- java21-modern
- python-async
TypeScript official site, TypeScript GitHub, TSConfig Reference, Strict Mode Family docs, DefinitelyTyped, TC39 ECMAScript proposals, zod, valibot, and ArkType are the references.