TypeScript 4.3 was announced on 26 May 2021. Its release notes are written for .ts files, but several changes reach JavaScript: a setter may accept more types than its getter returns, #private methods and accessors are checked, editors resolve the names in {@link} tags, and a promise tested in an if without await is reported.
It also added the override keyword and noImplicitOverride. In a .js file the keyword is the @override tag, which TypeScript 5.9 checks; the 4.3 notes do not mention the tag, so this site does not date it.
At a glance
| Release | TypeScript 4.3 |
|---|---|
| Released | 26 May 2021, the day of its announcement on the TypeScript blog |
| Source | TypeScript 4.3 release notes |
| Examples | Every example runs through TypeScript 5.9.3 when this site is built. |
| Previous release | TypeScript 4.2 |
| Next release | TypeScript 4.4 |
What changed for JavaScript
Setters with a wider type than their getters
A set size(value) can now take string | number | boolean while get size() returns number, as long as the getter's type is assignable to the setter's. In JavaScript, type the setter's parameter with @param and the getter with @returns.
export class Thing {
#size = 0;
/** @returns {number} */
get size() {
return this.#size;
}
/** @param {string | number | boolean} value */
set size(value) {
const n = Number(value);
this.#size = Number.isFinite(n) ? n : 0;
}
}
const thing = new Thing();
thing.size = "42";
/** @type {number} */
export const size = thing.size;
thing.size = null;error TS2322: Type 'null' is not assignable to type 'string | number | boolean'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} noImplicitOverride
With noImplicitOverride on, a method that replaces one from the base class must say so. In a .js file that is the @override tag; without it, TypeScript 5.9 reports TS4119.
export class Shape {
/** @returns {number} */
area() {
return 0;
}
}
export class Square extends Shape {
side = 2;
area() {error TS4119: This member must have a JSDoc comment with an '@override' tag because it overrides a member in the base class 'Shape'.
return this.side ** 2;
}
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"noImplicitOverride": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} {@link} in editors
Editors resolve the name inside a {@link} tag, so hovering it shows the target and go to definition jumps there. Nothing checks that the name exists; a broken link is not an error.
/**
* A shopping cart. Add lines with {@link Cart#add}, read the total with
* {@link cartTotal | the cartTotal function}, and see
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes classes on MDN}.
*/
export class Cart {
/** @type {{ price: number, quantity: number }[]} */
lines = [];
/**
* @param {number} price
* @param {number} quantity
*/
add(price, quantity) {
this.lines.push({ price, quantity });
}
}
/**
* Same as {@linkcode Cart} lines summed. See also {@linkplain Cart}.
* @param {Cart} cart
*/
export function cartTotal(cart) {
return cart.lines.reduce((sum, line) => sum + line.price * line.quantity, 0);
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} A promise used as a condition
Under strictNullChecks, a promise in an if condition is always truthy, so it is reported (TS2801). 4.3 reports a promise held in a variable; 4.4 extends it to a call tested directly.
/** @returns {Promise<boolean>} */
async function isAdmin() {
return false;
}
export async function greeting() {
const admin = isAdmin();
if (admin) {error TS2801: This condition will always return true since this 'Promise<boolean>' is always defined.
return "Welcome back";
}
return "Hello";
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} #private methods and accessors
Methods, getters and setters can have #private names as well as fields, and static members too. These are JavaScript features, checked like the rest: using one outside its class is an error.
export class Ticket {
static #issued = 0;
/** @param {string} title */
constructor(title) {
Ticket.#issued += 1;
this.label = this.#format(title);
}
/** @param {string} title */
#format(title) {
return `#${Ticket.#issued} ${title.trim()}`;
}
}
const ticket = new Ticket("Broken link");
ticket.#format("again");error TS18013: Property '#format' is not accessible outside class 'Ticket' because it has a private identifier.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} What did not change
@public, @private and @protected work as before; #private names are the run-time alternative.
Upgrade notes
From the breaking changes:
- Promises tested in conditions are errors under
strictNullChecks. useDefineForClassFieldsdefaults totruefortarget: esnext(andes2022once 4.6 added that target), which matches how JavaScript class fields work and only changes emitted code.- The DOM types dropped APIs no browser implements.
Gotchas
- Annotate the getter too. If the getter's type is inferred, TypeScript 5.9 treats the property as a
stringright after you assign a string through the setter, in.tsand.jsfiles alike;@returns {number}on the getter keeps it a number.
Sources
- TypeScript 4.3 release notes · TypeScript handbook
- Announcing TypeScript 4.3 · TypeScript blog
Last reviewed 20 October 2026.