Capítulo 5 de 36
Every property in an object type carries three independent axes — its type, whether it's optional, and whether it's writable — plus TypeScript layers extra structural machinery on top (index signatures, excess property checks, extends/intersection composition, generics, tuples) for describing the exact shape and mutability contract of real-world objects.
xPos?: number (optional — reads as T | undefined under strictNullChecks) and readonly prop: string (blocks reassignment during type-checking only, no runtime effect, and does not make nested objects immutable — home.resident.age++ is fine even if resident itself is readonly).readonly is not part of structural compatibility — a Person (mutable) is freely assignable to a ReadonlyPerson variable, and since it's the same underlying object, mutating through the original alias still changes what you read through the readonly-typed one. readonly signals intent, it isn't an immutability guarantee.{ shape: Shape, xPos: number } in a parameter pattern means something different in JS destructuring syntax (renaming), so the type goes after the whole pattern: function f({ shape, xPos }: PaintOptions).[index: string]: T / [index: number]: T): describe dictionary-shaped objects whose exact key set isn't known ahead of time. Only string, number, symbol, template-literal patterns, or unions of those are valid index key types. A string index signature forces every named property to be assignable to the index's value type (name: string conflicts with [index: string]: number) — widen the index to a union (number | string) to allow mixed property types. Index signatures can be readonly.colour instead of color is a compile error, even though normal structural typing would otherwise allow it (a supertype with extra properties is usually fine). Bypass via a type assertion, a string index signature ([propName: string]: unknown), or by first assigning the literal to an untyped variable (only works if there's still a common property TS can match against).interface B extends A { ... }): copies A's members into B, avoiding duplication; interfaces can extend multiple types at once (extends Colorful, Circle).A & B): combine object types via & on a type alias — the main way type composes since it lacks extends. Where interface-merging on a conflicting property name is a hard compile error, an intersection on conflicting property types ({ name: string } & { name: number }) silently collapses that property to never — a value that can now never actually satisfy the type, which is the real key difference between the two composition mechanisms.interface Box<Type> { contents: Type }): a reusable template — Box<string> substitutes Type with string. Avoids the boilerplate of hand-writing StringBox/NumberBox/BooleanBox variants and matching function overloads; a single function setContents<Type>(box: Box<Type>, newContents: Type) covers every instantiation. Type aliases can be generic too (type OrNull<Type> = Type | null), and unlike interfaces can express non-object generics (unions, helper types).Array<Type> is itself a generic interface — string[] is shorthand for Array<string>. Other built-in generic containers: Map<K, V>, Set<T>, Promise<T>.ReadonlyArray<T> / readonly T[]: no new constructor of its own — created by assigning a regular array to it. Assignability is one-directional: a mutable T[] is assignable to readonly T[], but not the reverse (compile error), unlike the bidirectional readonly property case above.[string, number]): fixed-length, fixed-position arrays with no runtime representation of their own — indexing past the declared length is a compile error. Support optional elements at the end ([number, number, number?], which also affects the inferred length type) and a rest element ([string, number, ...boolean[]], which makes length open-ended). Tuples with rest elements correspond directly to rest-parameter lists, letting a function signature enforce "at least N required args, then any number more of type X."readonly tuples (readonly [string, number]): blocks index writes. as const on an array literal infers a readonly tuple automatically — this is why a [3, 4] as const won't satisfy a plain mutable [number, number] parameter without an explicit cast; the function could mutate it, and TS can't guarantee that won't happen.interface Box<Type> {
contents: Type;
}
function setContents<Type>(box: Box<Type>, newContents: Type) {
box.contents = newContents;
}
contents type.type StringNumberBooleans = [string, number, ...boolean[]];
function readButtonInput(...args: StringNumberBooleans) {
const [name, version, ...input] = args;
}
name: string, version: number) plus any number of trailing booleans.| Construct | Composition keyword | Conflict behavior |
|---|---|---|
interface extends | extends | same-name incompatible properties → hard error |
Intersection (type) | & | same-name incompatible properties → property silently becomes never |
| Index signature | [k: string]: T | every named property must be assignable to T (union T to allow mixed) |
readonly and ReadonlyArray/readonly T[] are compile-time-only signals of intent, not runtime immutability — mutation through an aliased mutable reference still works.interface extends when composing plain object shapes (clearer error messages, catches genuine property conflicts); reach for intersection types when at least one side isn't a plain object type, keeping in mind conflicting properties resolve to never instead of erroring.readonly tuples/arrays by default for data that's created once and not mutated — as const gets you there for free on literals.as const first introduced there, used more deeply here.Box<Type>.readonly and ? modifiers programmatically across every property of a type.