TypeScript 2.0 introduced the same design as strictNullChecks, which gives null and undefined types of their own. The flag is part of strict, the default since TypeScript 6.0, so today's tsc (7.0.2, the native compiler) reports this error with no tsconfig.json at all:
interface Book { id: number; title: string; subtitle?: string }
const catalog: Book[] = [{ id: 1, title: "The Quiet Harbor" }];
const find = (id: number): Book | undefined => catalog.find((b) => b.id === id);
console.log(find(1)?.subtitle?.toLowerCase() ?? "no subtitle");
const forced = find(99)!; // TypeScript's ! erases to nothing at runtime
console.log(typeof forced);
console.log(find(99).title);Output
nulls.ts(7,13): error TS2532: Object is possibly 'undefined'. no subtitle undefined ... TypeError: Cannot read properties of undefined (reading 'title') ...
The first line is from tsc nulls.ts, the rest from node nulls.js: tsc still emits JavaScript, which crashes where the compiler warned. Line 5 shows the deepest difference: TypeScript's ! vanishes from the output, so forced silently holds undefined and the failure surfaces later, while Kotlin's !! throws on the spot.
| Feature | TypeScript | Kotlin |
|---|---|---|
| Nullable type | T | null, T | undefined, x?: T | T? (null only) |
| Safe access, default | a?.b, a ?? b | a?.b, a ?: b |
| Forced access | a!, no run-time check | a!!, throws on null |
| Narrowing | control-flow narrowing | smart casts |
| Can be switched off | yes, strictNullChecks: false | no |
| Unchecked values | any | platform types from Java |