Back

Exciting New Features in TypeScript 5.5

Exciting New Features in TypeScript 5.5

TypeScript has become an essential tool for modern web development. It is a language built on JavaScript that allows developers to declare and describe types. TypeScript 5.5, the latest iteration of Microsoft’s type-infused superset of JavaScript, comes with new features compared to its previous update, 5.4, and introduces a better approach to writing code, as this article shows.

Writing types in our code helps explain intent and catch mistakes like typos, null/undefined issues, etc. Some notable features in version 5.5 include inferred type predicates, regular expression syntax checking, advancements in array filtering, and more.

This article explores four of these improvements and compares them to the limitations of version 5.4. These features empower developers to write safer, more maintainable code, making it a vital tool in modern web development. The new features are significant:

  • Improved Type Checking: The latest version refines type-checking algorithms, catching more subtle errors during compilation. Developers benefit from better type inference, leading to safer code.

  • Enhanced Developer Experience: New features like inferred type predicates and control flow narrowing streamline development, making code easier to read and maintain.

  • Compatibility with Existing Codebases: The language maintains backward compatibility with previous updates. Upgrading to 5.5 doesn’t require major code changes, ensuring a smooth transition.

  • Handling Breaking Changes and Deprecations: Version 5.5 addresses deprecated features from earlier versions. Developers can adapt their code to align with the latest best practices.

Comparison to TypeScript 5.4

The new version empowers developers with better tools, smoother transitions, and improved code quality. Some of its improvements include:

  • Inferred Type Predicates: Unlike its predecessor, this update improves type tracking for variables, allowing better inference of types as they move through code. This enhancement catches more subtle errors during compilation, leading to safer code.

  • Control flow narrowing for constant-indexed Accesses: Enhancements in control flow analysis for constant-indexed accesses result in more accurate type narrowing when accessing properties dynamically.

  • Regular expression Syntax Checking: Basic syntax validation on regular expressions improves performance. It balances flexibility and correctness, allowing more expressive patterns without sacrificing safety.

  • Performance Optimizations: Faster build and iteration times improve productivity during development.

  • New EcmaScript Set Methods: It introduces union, intersection, difference, and symmetric difference methods for Sets. These Methods make set operations more convenient and efficient.

New Features in Version 5.5

This update brings a variety of features, ensuring developers benefit from safer code, enhanced productivity, and improved language features. Here, we discuss four important features:

  • Improvement in Array Filter.
  • Object Key inference Fixes.
  • Regular expression features.
  • Set Methods.

Improvement in Array Filter

Version 5.5 has enhanced type inference capabilities, particularly with the filter method. There have been significant improvements to theArray.prototype.filter method compared to the previous version. Previously, there was a struggle to infer the types correctly when filtering out null or undefined values. For example, filtering an array to remove null values did not automatically narrow the type of the resulting array.

constnums = [1, 2, null, 4];
constfilteredNums = nums.filter(num => num !== null);
// filteredNums is inferred as (number | null)[]

The type of filteredNums remains (number | null)[], even though we filtered out null values. This means you might need additional type checks later in your code.

However, in the latest iteration, this issue has been resolved. Using a type predicate (num is number) infers that filteredNums is number[], eliminating the need for further type checks.

constnums: (number | null)[] = [1, 2, null, 4];
constfilteredNums = nums.filter((num): num is number => num !== null);
// filteredNums is correctly inferred as number[]

The language recognizes the predicate type, allowing you to filter out null values from the array without encountering errors.

Object Key Inference Fixes

There are a couple of notable improvements related to object key inference. Consider the following example:

//Initial Behavior (Issue)
type MustBeArray<T extends any[]> = T;
type Hmm<T extends any[]> = T extends number[] ? MustBeArray<{ [I in keyof T]: 1 }> : never;

// This produces an error in previous releases:
type Z = Hmm<[2, 3, 4]>; // Error: Type '{ [I in keyof T]: 1; }' does not satisfy the constraint 'any[]'.

In the code above, there is a problem due to the intersection type MustBeArray<{ [I in keyof T]: 1 }> not satisfying the constraint 'any[]'. It caused unexpected errors when using mapped types with certain type conditions.

This issue has been resolved. TypeScript now correctly infers the predicate type for object keys. Rewriting this example to avoid errors, we obtain:

// Improvement in the current release
type Hmm<T extends any[]> = T extends number[] ? { [I in keyof T]: 1 } : never;
type Z = Hmm<[2, 3, 4]>; // Result: [1, 1, 1]

Now, the mapped type { [I in keyof T]: 1 } satisfies the constraint, and Z is correctly inferred as [1, 1, 1]. Z is inferred as [1, 1, 1] because [2, 3, 4] satisfies the constraint of being an array of numbers.

Regular Expression Features

It is now possible to perform basic syntax checking on regular expressions while maintaining strict error handling for questionable escapes. The current release strikes a balance between flexibility and correctness. This enhancement allows developers to write regular expressions without sacrificing error detection.

Example 1: Syntax Enhancements

In the most recent update, you can write more expressive regular expressions without unnecessary restrictions. Consider the following example:

// Version 5.5
const regex55 = /\d{2,4}/; // Matches 2 to 4 digits
console.log(regex55.test("123")); // true
console.log(regex55.test("12345")); // true
console.log(regex55.test("1")); // false (less than 2 digits)

The same regular expression would trigger an error in previous versions due to stricter syntax checking.

Example 2: Compatibility with Existing Patterns

The latest release maintains compatibility with existing regular expression patterns. Let’s compare it to older updates.

// Current Version
const regex55 = /[a-z]+/i; // Case-insensitive match for letters
console.log(regex55.test("Hello")); // true
console.log(regex55.test("WORLD")); // true

// Previous update (would also work)
const regex54 = /[a-z]+/i;
console.log(regex54.test("Hello")); // true
console.log(regex54.test("WORLD")); // true

Both versions allow case-insensitive matching, but version 5.5 provides more flexibility.

The latest release enhances code reliability and reduces debugging time by ensuring that regular expressions adhere to ECMAScript standards.

Set Methods and Improvements

The Set object provides a convenient way to manage unique values. Whether you’re working with strings, numbers, or custom objects, it ensures that there are no duplicates and that value handling is efficient.

What Developers Can Do with Sets:

  • Creating a Set: A Set is a collection of distinct values (no duplicates). You can create an empty Set or initialize it with initial values.

  • Adding, Retrieving, and Deleting Values: Use set.add(v) to add a value to the Set. You can also check if a value exists using set.has(v). To remove a value with set.delete(v). Clear all values using set.clear().

  • Iterating: Use for-of or forEach loops to iterate through the Set.

Examples

// Create with initial values.
const colors = new Set<string>(['red', 'green', 'blue']);

// Add more colors
colors.add('yellow');
colors.add('green'); // Ignored (no duplicates).

// Check if a color exists
console.log(colors.has('blue')); // true
console.log(colors.has('purple')); // false

// Remove a color
colors.delete('red'); // Returns true if successful.

// Iterate over the Set
for (const color of colors) {
console.log(color);
}

// Using forEach
colors.forEach((color) => {
console.log(color);
});

// Example with custom types (using objects)
type Person = { name: string };
const people = new Set<Person>();
const john: Person = { name: "John" };
const jane: Person = { name: "Jane" };

people.add(john);
people.add(jane);

console.log(people); // Two objects are present with the same content.

From the code above, we can see that managing sets is straightforward and efficient. You can create a Set with initial values and add more elements using the add method, which ensures no duplicates are added.

The has method allows you to check for the existence of specific values, while the delete method lets you remove elements. Iterating over a Set can be done using a for...of loop or the forEach method, making it easy to process each element.

Additionally, the new update supports sets with custom types, such as objects. For example, you can create a Set of Person objects and add instances to it. This feature is particularly useful for managing collections of complex data types, ensuring that each object is unique within the set.

Overall, these capabilities make Set a powerful and versatile data structure.

Additional Set Features

Version 5.5 introduces several new methods for Set objects unavailable in the older updates. These new methods align with the latest ECMAScript standards and enhance the Set’s functionality. Here are some of the key additions:

Union

The union method combines two sets into one, ensuring all unique elements are included.

Here’s a code illustrating this:

let setA: Set<number> = new Set([1, 2, 3]);
let setB: Set<number> = new Set([3, 4, 5]);
let unionSet: Set<number> = new Set([...setA, ...setB]);
console.log(unionSet);

The code above results in an output of:

Set { 1, 2, 3, 4, 5 }

The union method merges setA and setB into a new set unionSet, containing all unique elements from both sets.

Intersection

The intersection method creates a new set with elements present in both sets.

let intersectionSet: Set<number> = new Set([...setA].filter(x => setB.has(x)));
console.log(intersectionSet);

The output of the code is given below:

Set { 3 }

The intersection method filters elements that are common to both setA and setB, resulting in a new set intersectionSet with those elements.

Difference

The difference method returns elements in the first set but not in the second.

let differenceSet: Set<number> = new Set([...setA].filter(x => !setB.has(x)));
console.log(differenceSet);

Output:

Set { 1, 2 }

The difference method filters elements that are in setA but not in setB, creating a new set differenceSet with those elements.

Symmetric Difference

The symmetricDifference method finds elements in either set but not both.

let symmetricDifferenceSet: Set<number> = new Set([...setA].filter(x => !setB.has(x)).concat([...setB].filter(x => !setA.has(x))));
console.log(symmetricDifferenceSet);

The code above results in the output below:

Set { 1, 2, 4, 5 }

The symmetricDifference method combines elements that are in either setA or setB but not in both, resulting in a new set symmetricDifferenceSet.

isSubsetOf

The isSubsetOf method checks if all elements of one set are in another.

let isSubset: boolean = [...setA].every(x => setB.has(x));
console.log(isSubset);

The isSubsetOf method checks if every element in setA is also in setB, returning false because not all elements of setA are present in setB.

These examples demonstrate how each method works and what the expected outputs are. The new Set methods introduced, such as union, intersection, difference, symmetricDifference, and isSubsetOf, significantly enhance the ease and efficiency of working with sets. In previous releases, you would need to manually implement these operations using more verbose and less intuitive code. The new methods streamline these operations, making the code cleaner, more readable, and less error-prone.

For example, performing a union operation in TypeScript 5.4 requires using the spread operator and manually combining sets, whereas the new update, 5.5, provides a direct method for this. Similarly, intersection, difference, and symmetric difference operations are simplified with built-in methods, reducing the need for complex filtering and concatenation logic.

These enhancements not only save development time but also improve code maintainability and readability, making it easier for developers to work with sets in a more intuitive and efficient manner.

Conclusion

In this article, we have covered some of the new features of version 5.5 which was released on the 20th of June 2024. The features discussed display new updates, allowing developers to work with optional types in javascript, making it suitable for large-scale applications across browsers. This update is essential for efficient development and maintaining compatibility with the broader JavaScript ecosystem.

Resources

To learn more, you can visit the sites below by clicking on the link:

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay