TypeScript 5.3 is not a revolution but an evolution. The new TypeScript version provides a few important changes in key areas for developers, such as type narrowing and performance fixes. Overall, the new TypeScript version has a significant, positive impact on the developer experience.
switch(true) narrowing
Switch statements allow us to use expressions in addition to values. This pattern can be used in many situations to improve code quality and readability. The most common use case for this is replacing complex if/else statements. TypeScript 5.3 provides better narrowing in this specific situation. Now your logic can depend on type of variable and you will not get an error during compilation. Look at the following example:
function f(x: unknown) {
switch (true) {
case typeof value === 'string':
console.log("value is a 'string' here");
case Array.isArray(x):
console.log("value is a 'string | any[]' here");
default:
console.log("value is a 'unknown' here");
}
}
True/False direct comparison narrowing
Sometimes, for code reability reasons, we want to use a direct comparison to true/false values in If statements. Typescript 5.3 provides better narrowing in those statements. In example code below before changes you will get an error that property propOnlyInFirstInterface
not exist, now in Typescript 5.3 Typescript know that x is instance of interface FirstInterface
and propOnlyInFirstInterface
is defined, so you will not get an error during compilation.
interface FirstInterface {
propOnlyInFirstInterface: string;
}
interface SecondInterface {
propOnlyInSecondInterface: string;
}
type FirstOrSecondInterfaceType = FirstInterface | SecondInterface;
function isFirstInterfaceInstance(
x: FirstOrSecondInterfaceType
): x is FirstInterface {
return 'propOnlyInFirstInterface' in x;
}
function exampleFunction(x: FirstOrSecondInterfaceType) {
if (isFirstInterfaceInstance(x) === true) {
console.log(x.propOnlyInFirstInterface); // works, no errors during compilation
}
}
Narrowed Symbol.hasInstance checks
Typescript 5.3 introduces improvements for narrowing instance of
checks when they are used with user-defined classes. The compiler now narrows the type based on the value returned by the Symbol.hasInstance method.
Super property access checks
In Typescript 5.3, the compiler performs stricted accessibility checks on base class properties using the super keyword. First, before we will try to understand that improvement we must remember that the calling function with super keyword only works with members declared in the prototype. So if we declare something as a field it will not exist in prototype and during execution it will fail. Simpler, we can call only something which is method of class when we are using super keyword. In TypeScript 5.3 this is caught during compilation. For example:
class BaseClass {
field = () => {
console.log('field executed!');
};
}
class Example extends BaseClass {
method() {
super.field(); //throw type-checking error
}
}
new Example().method();
Skipping JSDoc parsing
In order to improve performance, when TypeScript is run through tsc, the compiler will not waste resources parsing JSDoc. Because of this, you get faster compile times in –watch mode.
tsserverlibrary.js and typescript.js consolidation
In Typescript 5.3, the contents of tsserverlibrary.js are now included within typescript.js. The old tsserverlibrary.js file now simply re-exports typescript.js. Previously, both of these modules had a lot of duplicated code, and it was very easy to load both of them. As you can see in the table below, this change reduces the overall package size by over 20%.
Before | After | Diff | |
Packed | 6.90 MiB | 5.48 MiB | -1.42 MiB( -20.61% ) |
Unpacked | 38.74 MiB | 30.41 MiB | -8.33 MiB( -21.50% ) |
Replacing import assertions with import attributes
TypeScript 5.3 allows you to provide information for both the browser and the runtime on how to treat imports. If you’re importing a JSON file, you can add type information to the statement. This can be also used in dynamic imports. From now on, it’s recommended that you use the new import keyword rather than the old asset. For instance, a static import will look like this:
import mockedData from "./mocked-data.json" with { type: "json" };
A dynamic import, on the other hand, will look like this:
const mockedData = await import("./mocked-data.json", {
with: { type: "json" }
});
Support for resolution-mode in import types
The resolution-mode feature allows you to specify if importing should be handled using the older require method, or the new import one. This was first introduced in Typescript 4.7, but now it has been included within the stable release. TypeScript 5.3 additionally supports this feature as an attribute for import types, for instance:
// Resolve `pkg` as if we were importing with a `require()`
import type { TypeFromRequire } from "pkg" with {
"resolution-mode": "require"
};
// Resolve `pkg` as if we were importing with an `import`
import type { TypeFromImport } from "pkg" with {
"resolution-mode": "import"
};
In-editor value type interactions
In Typescript 5.3 you can easily go to type definitions within your editor. For example, if you’re mapping elements from an array, you can hover over the predicted element type and then go to the definition. This feature is a godsend for those of us who expect to do a lot of refactoring, as well as those of us who work with complex projects with multiple data types. If you want to try that feature best IDE for that will be Visual studio code in except of other IDE( for example WebStorm) it doesnt have a lot of features provided by itself. Thats why every feature provided by Typescript in supporting IDE area have a lot of impact of increasing Dev experience in light IDE like VSC.
Summary
The changes introduced in TypeScript 5.3 go a long way to improve type narrowing and performance. It’s clear that the team behind the language prioritizes developer experience — and that’s commendable!
What’s next for TypeScript? While we don’t have any concrete announcements yet, rumour has it that version 5.4 will probably introduce changes like a reduction in the number of intersections between variable types and primitive types, as well as more accurate conditional type constraints. However, this is just speculation — we will just have to wait and see!