Item 11: Distinguish Excess Property Checking from Type Checking
May 10, 2024 ยท View on GitHub
Things to Remember
- When you assign an object literal to a variable with a known type or pass it as an argument to a function, it undergoes excess property checking.
- Excess property checking is an effective way to find errors, but it is distinct from the usual structural assignability checks done by the TypeScript type checker. Conflating these processes will make it harder for you to build a mental model of assignability. TypeScript types are not "closed" (pass:[Item 4]).
- Be aware of the limits of excess property checking: introducing an intermediate variable will remove these checks.
- A "weak type" is an object type with only optional properties. For these types, assignability checks require at least one matching property.
Code Samples
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'
};
const obj = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present',
};
const r: Room = obj; // OK
interface Options {
title: string;
darkMode?: boolean;
}
function createWindow(options: Options) {
if (options.darkMode) {
setDarkMode();
}
// ...
}
createWindow({
title: 'Spider Solitaire',
darkmode: true
// ~~~~~~~ Object literal may only specify known properties,
// but 'darkmode' does not exist in type 'Options'.
// Did you mean to write 'darkMode'?
});
const o1: Options = document; // OK
const o2: Options = new HTMLAnchorElement(); // OK
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
const o = { darkmode: true, title: 'MS Hearts' } as Options; // OK
interface Options {
darkMode?: boolean;
[otherOptions: string]: unknown;
}
const o: Options = { darkmode: true }; // OK
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'