The @implements tag asks TypeScript to check a class against a type: every member the type requires must exist on the class with a compatible type. TS2420 reports the class, and the lines under it name the missing or mismatched member.
It is the JSDoc form of class MemoryStore implements Store in a .ts file, and it is reported on the class even when nothing else uses it as a Store yet, which is the point of the tag.
At a glance
| Code | TS2420 |
|---|---|
| Message | Class '{0}' incorrectly implements interface '{1}'. |
| TypeScript's category | error |
| Listed under | Classes, JSX and unused code |
| Turned on by | No option: every checked file reports it |
| In .js files | Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file |
The message is TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} and {1} are filled in by tsc for each error.
What it looks like
store.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026:
store.js(9,14): error TS2420: Class 'MemoryStore' incorrectly implements interface 'Store'.
Property 'save' is missing in type 'MemoryStore' but required in type 'Store'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3.
store.js(9,14): error TS2420: Class 'MemoryStore' incorrectly implements interface 'Store'.
Property 'save' is missing in type 'MemoryStore' but required in type 'Store'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3.
store.js(9,14): error TS2420: Class 'MemoryStore' incorrectly implements interface 'Store'.
Property 'save' is missing in type 'MemoryStore' but required in type 'Store'. checked 21 October 2026
/**
* @typedef {object} Store
* @property {(id: string) => Promise<string | undefined>} load
* @property {(id: string, value: string) => Promise<void>} save
*/
/** @implements {Store} */
export class MemoryStore {error TS2420: Class 'MemoryStore' incorrectly implements interface 'Store'.
Property 'save' is missing in type 'MemoryStore' but required in type 'Store'.
/** @type {Map<string, string>} */
#items = new Map();
/** @param {string} id */
async load(id) {
return this.#items.get(id);
}
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["**/*.js"]
} What causes it
- A method of the type is missing from the class.
- A member exists with an incompatible type (then TS2416 may appear on the member too).
How to fix it
Add the missing member
Implement every member the type lists, with compatible types.
store.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026:
No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3.
No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3.
No errors. checked 21 October 2026
/**
* @typedef {object} Store
* @property {(id: string) => Promise<string | undefined>} load
* @property {(id: string, value: string) => Promise<void>} save
*/
/** @implements {Store} */
export class MemoryStore {
/** @type {Map<string, string>} */
#items = new Map();
/** @param {string} id */
async load(id) {
return this.#items.get(id);
}
/**
* @param {string} id
* @param {string} value
*/
async save(id, value) {
this.#items.set(id, value);
}
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["**/*.js"]
} Gotchas
@implementschecks the class but does not type its methods' parameters: each still needs its own@param(TS7006 otherwise), as the recipe shows.
Sources
- JSDoc Reference: @implements · TypeScript handbook
- diagnosticMessages.json (TypeScript 5.9.3) · TypeScript on GitHub
Last reviewed 21 October 2026.