// Inference vs annotation — annotate inputs, let outputs inferfunctiondouble(n: number) { // ✅ annotate the parameter (a boundary)return n * 2; // return type inferred as number — no annotation needed
}
let a = 3; // number (widened, because let is mutable)const b = 3; // 3 (literal type kept, because const can't change)// ❌ any turns off the type checker silentlyconstraw: any = JSON.parse('{"n": 1}');
constwrong: number = raw.doesNotExist.atAll; // compiles! crashes at runtime.// ✅ types are erased — this is what actually shipsfunctiongreet(name: string): string { return`hi ${name}`; }
// compiles to: function greet(name) { return `hi ${name}`; }
// The boundary problem: TS trusts your annotation, the network doesn'tconst user = awaitfetch('/api/me').then(r => r.json()); // r.json() is Promise<any>// user is `any` — every property access is unchecked. Validate here (see Zod).
Итог
Annotate boundaries; let/const меняет widening; any на границе отравляет модуль; типы не survive runtime.
TypeScript: основы типов и inference — пример кода · Sobeso