What an interface is
An interface gives a name to the shape of an object: which properties it has and what type each one holds. You write it once, and from then on the name works anywhere a type works: on variables, on function parameters and on return types.
interface User {
name: string;
age: number;
}
Until now, annotations described single values, as in let age: number = 31;. Real programs pass whole objects around, such as a user, a product or a point on a map. Without an interface, every function that receives such an object would have to be trusted to get the property names and types right. With an interface, the compiler checks each place that touches the object.
Using an interface
You attach the interface with the usual colon annotation, and you can use it as a parameter type:
interface User {
name: string;
age: number;
}
function greet(user: User): void {
console.log("Hello, " + user.name + "!");
}
const alice: User = { name: "Alice", age: 30 };
greet(alice);
This prints:
Hello, Alice!
Inside greet, the compiler knows that user has a name and an age. Your editor can suggest those property names, and a typo such as user.nmae is reported as an error before the program runs.
An interface exists only for the compiler. When TypeScript compiles to JavaScript, the interface block is removed completely and produces no code. Nothing checks the shape of an object while the program runs.
The shape must match
An object annotated with an interface must have every declared property. If one is missing, the compiler reports it:
const bob: User = { name: "Bob" };
error TS2741: Property 'age' is missing in type '{ name: string; }' but required in type 'User'.
When you assign an object literal directly, TypeScript also runs an excess property check: properties the interface does not mention are rejected too.
const eve: User = { name: "Eve", age: 22, city: "Paris" };
error TS2353: Object literal may only specify known properties, and 'city' does not exist in type 'User'.
Both messages name the property and the interface involved, so reading them tells you what to add or remove. That is the normal way interface mistakes get fixed.
The lesson's exercise, step by step
The goal is a program that prints two lines, name = Ada and age = 36.
- Declare the interface. Write
interface Userwith a body in braces. The interface is the contract that everyUserhas to follow. - Add the
namefield. Inside the braces, writename: string;. A field is a property name, a colon and a type, with no value. - Add the
agefield. In the same way,age: number;. - Define the function.
describetakes one parameteruserof typeUserand returnsvoid, which means it returns nothing. - Print the name. Inside
describe, useconsole.log("name = " + user.name). Becauseuseris aUser, the compiler guarantees that the field exists. - Print the age. On the next line,
console.log("age = " + user.age). The+turns the number into text and joins it to the label. - Create the object. Declare
const ada: Userand set it to an object literal with the name"Ada"and the age36. The annotation makes TypeScript check the literal immediately, so a missing or extra property is a compile error. - Call the function.
describe(ada)prints both lines.
The complete program:
interface User {
name: string;
age: number;
}
function describe(user: User): void {
console.log("name = " + user.name);
console.log("age = " + user.age);
}
const ada: User = { name: "Ada", age: 36 };
describe(ada);
Its output:
name = Ada
age = 36
Gotchas
- Separators. Fields inside an interface may end with
;or,. Both are accepted, and semicolons are the common style. Object literals use commas. - Passing a bad value still runs. The compiler reports type errors, but it still writes the JavaScript, and the lessons' tool runs it. Calling
describe({ name: "Q" })gets an error from the compiler, yet the program printsname = Qandage = undefined. Treat the red error as a bug to fix, not a warning. - Casts do not convert anything.
JSON.parse('{"name":"Zed"}') as Usercompiles without complaint, although the object has noage. The cast only tells the compiler to trust you, anduser.ageisundefinedat run time. Data from outside the program, such as JSON, is not checked by the interface. - The excess property check applies to literals. If you first store
{ name: "Kim", age: 5, city: "Rome" }in a variable and then pass the variable todescribe, there is no error. The object has everything aUserneeds, and extra properties are allowed when the value is not a fresh literal. - Optional and read-only fields. A property written as
y?: numbermay be left out, andreadonly id: stringcan be set when the object is created but not assigned to later. Assigning to it is a compile error. - Interfaces describe objects, not values. You can't write
User.namein a program. There is nothing at run time to read, andtypeof adais just"object".
Key takeaways
- An interface names an object shape: property names with their types.
- Annotating a variable or parameter with it makes the compiler check every use.
- Missing properties and, for fresh object literals, extra properties are compile errors.
- Interfaces are erased when compiling, so they never check data at run time.