interface Point { x: number; y: number; }
function printPoint(p: Point) {
console.log(`${p.x}, ${p.y}`);
}
const location = { x: 3, y: 4, label: "home" };
printPoint(location); // valid — location has at least x and y as numbers
location was never declared as a Point — TypeScript doesn't care. This is structural typing ("duck typing," checked at compile time): if a value has the right shape, it satisfies the type, regardless of name or explicit declaration. This is a real difference from languages like C# or Java, where a type has to be nominally declared as implementing an interface to satisfy it — see C# Fundamentals for the nominal side of that comparison.
Structural typing has a specific, commonly-tested gotcha with object literals: excess property checking.
interface Config { url: string; }
function connect(config: Config) { /* ... */ }
connect({ url: "https://api.example.com", timeout: 5000 });
// Error: Object literal may only specify known properties, and 'timeout'
// does not exist in type 'Config'.
const options = { url: "https://api.example.com", timeout: 5000 };
connect(options); // fine — no error, because `options` isn't a fresh literal
Both calls pass a value with an "excess" timeout property, and structurally, both should be fine — extra properties don't violate structural compatibility. But TypeScript special-cases object literals assigned or passed directly with extra checking, on the theory that a literal with a property the target type doesn't recognize is more likely a typo than an intentional excess field. Assigning it to a variable first (as options does) sidesteps that extra check, because a variable of inferred type is no longer a "fresh" literal at the call site.