Distinguish Excess Property Checking from Type Checking

Distinguish Excess Property Checking from Type Checking

// case1
interface Room {
 numDoors: number;
 ceilingHeightFt: number;
}
const r: Room = {
 numDoors: 1,
 ceilingHeightFt: 10,
 elephant: 'present',
// ~~~~~~~ Object literal may only specify known properties,
// and 'elephant' does not exist in type 'Room'
};

// case2
const obj = {
 numDoors: 1,
 ceilingHeightFt: 10,
 elephant: 'present',
};
const r1: Room = obj; // OK

So what’s different about these two examples? In the first you’ve triggered a process known as “excess property checking,” which helps catch an important class of errors that the structural type system would otherwise miss. But this process has its limits, and conflating it with regular assignability checks can make it harder to build an intuition for structural typing. Recognizing excess property checking as a distinct process will help you build a clearer mental model of TypeScript’s type system.

const o: Options = { darkmode: true, title: 'Ski Free' };
// ~~~~~~~~ 'darkmode' does not exist in type 'Options'...

const intermediate = { darkmode: true, title: 'Ski Free' };
const o: Options = intermediate; // OK

While the righthand side of the first line is an object literal, the righthand side of the second line (intermediate) is not, so excess property checking does not apply, and the error goes away.

Excess property checking does not happen when you use a type assertion:

const o = { darkmode: true, title: 'MS Hearts' } as Options; // OK

This is a good reason to prefer type annotations to assertions (Item 9).

If you don’t want this sort of check, you can tell TypeScript to expect additional properties using an index signature:

interface Options {
 darkMode?: boolean;
 [otherOptions: string]: unknown;
}
const o: Options = { darkmode: true }; // OK

A related check happens for so-called “weak” types, which have only optional properties:

interface LineChartOptions {
 logscale?: boolean;
 invertedYAxis?: boolean;
 areaChart?: boolean;
}
function setOptions(options: LineChartOptions) { /* ... */ }
const opts = { logScale: true };
setOptions(opts);
// ~~~~ Type '{ logScale: boolean; }' has no properties in common
// with type 'LineChartOptions'

Things to Remember