Reference · annotations, assertions, structural typing

Annotations, assertions & structural typing — decoder

Where a type comes from, how you attach it, and why a value is (or isn't) accepted. Find the source first; the behavior follows.

Three sources of a type (0.2)

sourcewho supplies ithow you spot it
annotationyou, explicitlya : then a type you typed
inferencethe compiler, from a valueno : — type came from the =
definitiona name you declared oncetype / interface / .d.ts
Maxim: annotation = a promise you made · inference = the compiler's guess from the value · definition = a reusable name that annotates nothing until attached. “Type information” is the umbrella — it covers annotated and inferred types.
interface User { id: number; name: string; }  // definition (named, reusable)
const u: User = { id: 1, name: "Ada" };       // annotation (you wrote : User)
let n = 5;                                // inference (n : number, derived from 5)

let   mode  = "dark";   // inferred string  — let widens the literal
const theme = "dark";   // inferred "dark"   — const keeps the literal

Attach a type three ways: annotation vs as vs satisfies (0.3)

syntaxcan add props later?keeps literal types?use case
const o: Type = …yesnoadd properties later
const o = … as Typeyesnoadd properties later
const o = … satisfies Typenoyescheck a literal at creation
Annotation & as → variable becomes the wide declared type (room to grow, literals lost). satisfieschecks the value, keeps its narrow literal type (no room to grow, detail kept). as can lie; satisfies cannotas asserts without checking, satisfies only verifies.
const a: Record<string, number> = {}; a.x = 1;  // OK  — wide Record type
const b = {} as Record<string, number>;   b.x = 1;  // OK  — wide, by assertion
const c = {} satisfies Record<string, number>; c.x = 1;// ERR — c is still {}, narrow

Structural typing & the excess-property rule (0.4)

how the value arrivescheck appliedextra props?
object literal passed inlineexcess-property check (typo guard)rejected
literal stored in a variable firstplain structural subtypingallowed
TS is structural (duck typing): a value fits a type if it has the required shape, extras allowed. A fresh literal handed straight to a slot gets a one-time excess-property check that flags unknown keys as likely typos. Inline ≈ strict assignment; variable ≈ subtyping (a richer subtype substitutes for the base).
interface FetchOptions { url: string; method: string; }
const myFetch = (o: FetchOptions) => {};

myFetch({ url: "/", method: "GET", search: x });  // ERR — inline: excess 'search'

const options = { url: "/", method: "GET", search: x };
myFetch(options);                                // OK  — variable: subtype, extras fine

The 3-second checks

  1. Where's the type from? Colon you wrote → annotation · no colon → inference · type/interface → definition.
  2. Wide or narrow? : Type / as Type → wide declared type · satisfies Type → narrow literal, checked.
  3. Why accepted/rejected? Inline literal → excess check (extras error) · via variable → structural subtyping (extras fine).