Note Interfaces define the shape of objects. They are structurally typed - any object with the right shape satisfies the interface, no explicit 'implements' needed for plain objects.
Note An optional property (prop?) means 'may be missing or undefined'. This is different from prop: Type | undefined, which requires the key to be present. With exactOptionalPropertyTypes in tsconfig, this distinction is enforced even more strictly.
optional propertyquestion mark propertyoptional fieldmaybe undefined
Readonly Properties
Syntax
interfaceName{readonly property:Type;}
Example
interfaceDatabaseRecord{readonly id: string;readonly createdAt:Date;
name: string;
updatedAt:Date;}const record:DatabaseRecord={
id:"rec_abc",
createdAt:newDate(),
name:"Initial",
updatedAt:newDate(),};
record.name="Updated";// OK// record.id = "rec_xyz"; // Error: Cannot assign to 'id' because it is read-only
Output
// readonly prevents reassignment at compile time only
Note readonly is a compile-time constraint - there is no runtime enforcement. Deep objects with readonly props can still have their nested properties mutated unless those are also readonly. Use Readonly<T> utility to make all properties readonly at once.
// Multiple inheritance via comma-separated parent interfaces
Note An interface can extend multiple other interfaces. If parent interfaces have conflicting property types, you get a compile error. Interfaces can also extend type aliases (as long as the alias resolves to an object shape).
// Class must define all properties/methods declared in the interface
Note implements does NOT add types to the class - it only checks that the class satisfies the shape. You must still annotate parameter types in the class methods. A class can implement multiple interfaces with comma separation.
interfaceTranslationMap{[locale: string]: string;}const greetings:TranslationMap={
en:"Hello",
es:"Hola",
ja:"こんにちは",};// Mixed: known + dynamic keysinterfaceAppConfig{
appName: string;
version: string;[key: string]: string;// all values must be string}
Output
// greetings["fr"] is string (even if missing at runtime)
Note Index signatures allow any key of the given type. When combined with named properties, all named property types must be compatible with the index signature type. Enable noUncheckedIndexedAccess in tsconfig to get T | undefined instead of just T for indexed access - much safer.
interfaceName{ propA:Type;}interfaceName{ propB:Type;}// Merged into one interface with both props
Example
// Original library typeinterfaceWindow{
title: string;}// Your augmentation - merges with the aboveinterfaceWindow{
analyticsId: string;}// Now Window has both title and analyticsIdconst w:Window={
title:"My App",
analyticsId:"UA-12345",};
Output
// Both declarations merge into a single interface
Note Declaration merging is unique to interfaces - type aliases cannot be re-opened. This is powerful for extending third-party types (like adding properties to Window or Express Request). The merged properties must not conflict in type.