|
| 1 | +--- |
| 2 | +layout: "@layouts/BlogPost.astro" |
| 3 | +title: "4 New TypeScript 5.5 Features" |
| 4 | +date: "2024-07-15" |
| 5 | +description: "TypeScript 5.5 may seem like a small update, but it comes with one of the most requested features for fixing array filtering as well as 3 other really cool features that I love." |
| 6 | +tags: ["TypeScript"] |
| 7 | +--- |
| 8 | + |
| 9 | +import Tangent from "@blogComponents/lib/Tangent.astro" |
| 10 | + |
| 11 | +## Introduction |
| 12 | + |
| 13 | +Unlike most libraries, TypeScript does not use semantic versioning which means major new features and breaking changes can be introduced in any version update. This is why version 5.5 can include such large changes even though it is not a major version update by semantic versioning standards. In this article, I will go over the 4 new features in TypeScript 5.5 that I think are the most interesting, but you can also read the [full release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/) yourself. |
| 14 | + |
| 15 | +<Tangent> |
| 16 | + If you have never heard of semantic versioning and want to learn more I have a |
| 17 | + full [semantic versioning article](/2020-01/semantic-versioning) you can read. |
| 18 | +</Tangent> |
| 19 | + |
| 20 | +## Array Filtering Improvement |
| 21 | + |
| 22 | +If you have ever tried to filter an array in TypeScript to narrow the types of items allowed you have most likely ran into the surprising result that the TypeScript type never changes. |
| 23 | + |
| 24 | +```typescript |
| 25 | +// v5.4 |
| 26 | +const array = [1, 2, 3, null] |
| 27 | + |
| 28 | +// TypeScript still thinks numbers is (number | null)[] |
| 29 | +// This means TypeScript still thinks our numbers array has null values |
| 30 | +const numbers = array.filter(item => item != null) |
| 31 | + |
| 32 | +numbers.forEach(number => { |
| 33 | + // TypeScript will error here because it thinks number could be null |
| 34 | + console.log(number + 1) |
| 35 | +}) |
| 36 | +``` |
| 37 | + |
| 38 | +When we look at this code it is obvious our `numbers` array no longer has `null` values, but TypeScript is not smart enough to realize this. That is until now. With TypeScript version 5.5 this code will now work as we expect. |
| 39 | + |
| 40 | +```typescript |
| 41 | +// v5.5 |
| 42 | +const array = [1, 2, 3, null] |
| 43 | + |
| 44 | +// TypeScript now knows that numbers is number[] |
| 45 | +const numbers = array.filter(item => item != null) |
| 46 | + |
| 47 | +numbers.forEach(number => { |
| 48 | + // No errors |
| 49 | + console.log(number + 1) |
| 50 | +}) |
| 51 | +``` |
| 52 | + |
| 53 | +This will even work if we pass another function to our `filter` function or have other more complex logic. |
| 54 | + |
| 55 | +```typescript |
| 56 | +// v5.5 |
| 57 | +const people = [{ name: "Kyle", age: 27 }, { name: "John" }] |
| 58 | + |
| 59 | +// TypeScript now knows that peopleWithAge is ({ name: string, age: number })[] |
| 60 | +// This means TypeScript knows that peopleWithAge only has objects with an age and name property |
| 61 | +const peopleWithAge = people.filter(person => person.age != null) |
| 62 | + |
| 63 | +peopleWithAge.forEach(person => { |
| 64 | + // No errors |
| 65 | + console.log(`${person.name} will be ${person.age + 1} years old next year`) |
| 66 | +}) |
| 67 | +``` |
| 68 | + |
| 69 | +## Object Key Inference Improvement |
| 70 | + |
| 71 | +Another problem that TypeScript used to have is they struggled to infer the type of an object that was accessed via an index after that object's index type had been narrowed. This sounds really complicated when written out so let's look at an example that makes this easy to understand. |
| 72 | + |
| 73 | +```typescript |
| 74 | +// v5.4 |
| 75 | +function upperCaseKey(obj: Record<string, unknown>, key: string) { |
| 76 | + if (typeof obj[key] === "string") { |
| 77 | + // Error: TypeScript still thinks obj[key] is unknown |
| 78 | + return obj[key].toUpperCase() |
| 79 | + } |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +As you can see in this code we have narrowed the type of `obj[key]` to be a string, but TypeScript is not smart enough to realize that this type has been narrowed inside the `if` block. This is why in version 5.4 we get an error. To get around this we would need to create a new variable to store the narrowed type. |
| 84 | + |
| 85 | +```typescript {3} |
| 86 | +// v5.4 |
| 87 | +function upperCaseKey(obj: Record<string, unknown>, key: string) { |
| 88 | + const value = obj[key] |
| 89 | + if (typeof value === "string") { |
| 90 | + // No errors |
| 91 | + return value.toUpperCase() |
| 92 | + } |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +This new code has no errors, but it is not ideal since we created a new variable in memory just to solve a TypeScript error. This is why in version 5.5 TypeScript has improved this behavior and made it so these index accessed types work with no changes needed. |
| 97 | + |
| 98 | +```typescript |
| 99 | +// v5.5 |
| 100 | +function upperCaseKey(obj: Record<string, unknown>, key: string) { |
| 101 | + if (typeof obj[key] === "string") { |
| 102 | + // No errors |
| 103 | + return obj[key].toUpperCase() |
| 104 | + } |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +## Regular Expression Checking |
| 109 | + |
| 110 | +In the previous versions of TypeScript if you had a regular expression in your code it was completely ignored by the TypeScript type checker. This meant that you could have a regular expression that was invalid and TypeScript would not tell about it. This is no longer the case in TypeScript 5.5, though, as TypeScript will check your regular expressions to let you know if there are any potential problems with them. |
| 111 | + |
| 112 | +```typescript |
| 113 | +const regex1 = /extra(parens))?/ |
| 114 | +// Error: Unexpected ')'. Did you mean to escape it with backslash? |
| 115 | + |
| 116 | +const regex2 = /capture(?<group>.+) \k<namedImport>/ |
| 117 | +// Error: There is no capturing group named 'namedImport' in this regular expression. |
| 118 | +``` |
| 119 | + |
| 120 | +This is just a small example of the many different errors that TypeScript can now catch with regular expressions. |
| 121 | + |
| 122 | +## New Set methods |
| 123 | + |
| 124 | +The final feature I want to talk about is the new methods that have been added to the `Set` class. TypeScript 5.5 has added support for all the new `Set` methods introduced to JavaScript recently. This includes the `intersection`, `union`, `difference`, and all other methods. |
| 125 | + |
| 126 | +```typescript |
| 127 | +const set1 = new Set([1, 2, 3]) |
| 128 | +const set2 = new Set([3, 4, 5]) |
| 129 | + |
| 130 | +const intersection = set1.intersection(set2) |
| 131 | +const union = set1.union(set2) |
| 132 | +const difference = set1.difference(set2) |
| 133 | +``` |
| 134 | + |
| 135 | +This is a nice quality of life feature since these new `Set` methods are really useful and now we can use them in TypeScript without any type issues. |
| 136 | + |
| 137 | +## Conclusion |
| 138 | + |
| 139 | +TypeScript 5.5 may seem like a small update, but it comes with quite a few quality of life improvements. The array filtering improvement is by far my favorite and I am glad to see it finally added. I cannot wait to see what TypeScript has in store for us in the future. |
0 commit comments