{"id":4270,"date":"2024-06-20T10:16:11","date_gmt":"2024-06-20T18:16:11","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/typescript\/?p=4270"},"modified":"2024-06-21T19:55:22","modified_gmt":"2024-06-22T03:55:22","slug":"announcing-typescript-5-5","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-5-5\/","title":{"rendered":"Announcing TypeScript 5.5"},"content":{"rendered":"<p>Today we&#8217;re excited to announce the release of TypeScript 5.5!<\/p>\n<p>If you&#8217;re not familiar with TypeScript, it&#8217;s a language that builds on top of JavaScript by making it possible to declare and describe types.\nWriting types in our code allows us to explain intent and have other tools check our code to catch mistakes like typos, issues with <code>null<\/code> and <code>undefined<\/code>, and more.\nTypes also power TypeScript&#8217;s editor tooling like the auto-completion, code navigation, and refactorings that you might see in editors like Visual Studio and VS Code.\nIn fact, if you write JavaScript in either of those editors, that experience is powered by TypeScript!\nYou can learn more at <a href=\"https:\/\/www.typescriptlang.org\/\">the TypeScript website<\/a>.<\/p>\n<p>To get started using TypeScript through npm with the following command:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>npm install -D typescript\r\n<\/code><\/pre>\n<p>or <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.TypeScript.MSBuild\">through NuGet<\/a>.<\/p>\n<p>Here&#8217;s a quick list of what&#8217;s new in TypeScript 5.5!<\/p>\n<ul>\n<li><a href=\"#inferred-type-predicates\">Inferred Type Predicates<\/a><\/li>\n<li><a href=\"#control-flow-narrowing-for-constant-indexed-accesses\">Control Flow Narrowing for Constant Indexed Accesses<\/a><\/li>\n<li><a href=\"#the-jsdoc-import-tag\">The JSDoc <code>@import<\/code> Tag<\/a><\/li>\n<li><a href=\"#regular-expression-syntax-checking\">Regular Expression Syntax Checking<\/a><\/li>\n<li><a href=\"#support-for-new-ecmascript-set-methods\">Support for New ECMAScript <code>Set<\/code> Methods<\/a><\/li>\n<li><a href=\"#isolated-declarations\">Isolated Declarations<\/a><\/li>\n<li><a href=\"#the-configdir-template-variable-for-configuration-files\">The <code>${configDir}<\/code> Template Variable for Configuration Files<\/a><\/li>\n<li><a href=\"#consulting-packagejson-dependencies-for-declaration-file-generation\">Consulting <code>package.json<\/code> Dependencies for Declaration File Generation<\/a><\/li>\n<li><a href=\"#editor-and-watch-mode-reliability-improvements\">Editor and Watch-Mode Reliability Improvements<\/a><\/li>\n<li><a href=\"#performance-and-size-optimizations\">Performance and Size Optimizations<\/a><\/li>\n<li><a href=\"#easier-api-consumption-from-ecmascript-modules\">Easier API Consumption from ECMAScript Modules<\/a><\/li>\n<li><a href=\"#the-transpiledeclaration-api\">The <code>transpileDeclaration<\/code> API<\/a><\/li>\n<li><a href=\"#notable-behavioral-changes\">Notable Behavioral Changes<\/a>\n<ul>\n<li><a href=\"#disabling-features-deprecated-in-typescript-50\">Disabling Features Deprecated in TypeScript 5.0<\/a><\/li>\n<li><a href=\"#libdts-changes\"><code>lib.d.ts<\/code> Changes<\/a><\/li>\n<li><a href=\"#stricter-parsing-for-decorators\">Stricter Parsing for Decorators<\/a><\/li>\n<li><a href=\"#undefined-is-no-longer-a-definable-type-name\"><code>undefined<\/code> is No Longer a Definable Type Name<\/a><\/li>\n<li><a href=\"#simplified-reference-directive-declaration-emit\">Simplified Reference Directive Declaration Emit<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2 id=\"whats-new-since-the-beta-and-rc\">What&#8217;s New Since the Beta and RC?<\/h2>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-5-5-rc\/\">Since the beta<\/a>, we&#8217;ve made a few changes that we wanted to call out.<\/p>\n<p>For one, we <a href=\"#support-for-new-set-methods\">added support for ECMAScript&#8217;s new <code>Set<\/code> methods<\/a>. Additionally, we&#8217;ve adjusted the behavior of <a href=\"#regular-expression-syntax-checking\">TypeScript&#8217;s new regular expression checking<\/a> to be slightly more lenient, while still erroring on questionable escapes that are only allowed per ECMAScript&#8217;s Annex B.<\/p>\n<p>We&#8217;ve also added and documented even more <a href=\"#performance-and-size-optimizations\">performance optimizations<\/a>: notably, skipped checking in <code>transpileModule<\/code> and optimizations in how TypeScript filters contextual types.\nThese optimizations can lead to faster build and iteration time in many common scenarios.<\/p>\n<p>Since <a href=\"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-5-5-rc\/\">the release candidate (RC)<\/a>, we&#8217;ve temporarily reverted <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57896\">our newer work around consulting <code>package.json<\/code> to determine a given file&#8217;s module format<\/a>. We received some feedback that this change disrupted certain workflows and caused an unexpected amount of file-watching pressure for larger projects. In TypeScript 5.6, we hope to bring back <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58825\">a more nuanced version of this feature<\/a>, while we also look into optimizations how we can watch for non-existent files.<\/p>\n<h2 id=\"inferred-type-predicates\">Inferred Type Predicates<\/h2>\n<p><em>This section was written by <a href=\"https:\/\/github.com\/danvk\">Dan Vanderkam<\/a>, who <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57465\">implemented this feature in TypeScript 5.5<\/a>. Thanks Dan!<\/em><\/p>\n<p>TypeScript&#8217;s control flow analysis does a great job of tracking how the type of a variable changes as it moves through your code:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>interface Bird {\r\n    commonName: string;\r\n    scientificName: string;\r\n    sing(): void;\r\n}\r\n\r\n\/\/ Maps country names -&gt; national bird.\r\n\/\/ Not all nations have official birds (looking at you, Canada!)\r\ndeclare const nationalBirds: Map&lt;string, Bird&gt;;\r\n\r\nfunction makeNationalBirdCall(country: string) {\r\n  const bird = nationalBirds.get(country);  \/\/ bird has a declared type of Bird | undefined\r\n  if (bird) {\r\n    bird.sing();  \/\/ bird has type Bird inside the if statement\r\n  } else {\r\n    \/\/ bird has type undefined here.\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>By making you handle the <code>undefined<\/code> case, TypeScript pushes you to write more robust code.<\/p>\n<p>In the past, this sort of type refinement was more difficult to apply to arrays. This would have been an error in all previous versions of TypeScript:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>function makeBirdCalls(countries: string[]) {\r\n  \/\/ birds: (Bird | undefined)[]\r\n  const birds = countries\r\n    .map(country =&gt; nationalBirds.get(country))\r\n    .filter(bird =&gt; bird !== undefined);\r\n\r\n  for (const bird of birds) {\r\n    bird.sing();  \/\/ error: 'bird' is possibly 'undefined'.\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>This code is perfectly fine: we&#8217;ve filtered all the <code>undefined<\/code> values out of the list.\nBut TypeScript hasn&#8217;t been able to follow along.<\/p>\n<p>With TypeScript 5.5, the type checker is fine with this code:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>function makeBirdCalls(countries: string[]) {\r\n  \/\/ birds: Bird[]\r\n  const birds = countries\r\n    .map(country =&gt; nationalBirds.get(country))\r\n    .filter(bird =&gt; bird !== undefined);\r\n\r\n  for (const bird of birds) {\r\n    bird.sing();  \/\/ ok!\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>Note the more precise type for <code>birds<\/code>.<\/p>\n<p>This works because TypeScript now infers a <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/2\/narrowing.html#using-type-predicates\">type predicate<\/a> for the <code>filter<\/code> function.\nYou can see what&#8217;s going on more clearly by pulling it out into a standalone function:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ function isBirdReal(bird: Bird | undefined): bird is Bird\r\nfunction isBirdReal(bird: Bird | undefined) {\r\n  return bird !== undefined;\r\n}\r\n<\/code><\/pre>\n<p><code>bird is Bird<\/code> is the type predicate.\nIt means that, if the function returns <code>true<\/code>, then it&#8217;s a <code>Bird<\/code> (if the function returns <code>false<\/code> then it&#8217;s <code>undefined<\/code>).\nThe type declarations for <code>Array.prototype.filter<\/code> know about type predicates, so the net result is that you get a more precise type and the code passes the type checker.<\/p>\n<p>TypeScript will infer that a function returns a type predicate if these conditions hold:<\/p>\n<ol>\n<li>The function does not have an explicit return type or type predicate annotation.<\/li>\n<li>The function has a single <code>return<\/code> statement and no implicit returns.<\/li>\n<li>The function does not mutate its parameter.<\/li>\n<li>The function returns a <code>boolean<\/code> expression that&#8217;s tied to a refinement on the parameter.<\/li>\n<\/ol>\n<p>Generally this works how you&#8217;d expect.\nHere&#8217;s a few more examples of inferred type predicates:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ const isNumber: (x: unknown) =&gt; x is number\r\nconst isNumber = (x: unknown) =&gt; typeof x === 'number';\r\n\r\n\/\/ const isNonNullish: &lt;T&gt;(x: T) =&gt; x is NonNullable&lt;T&gt;\r\nconst isNonNullish = &lt;T,&gt;(x: T) =&gt; x != null;\r\n<\/code><\/pre>\n<p>Previously, TypeScript would have just inferred that these functions return <code>boolean<\/code>.\nIt now infers signatures with type predicates like <code>x is number<\/code> or <code>x is NonNullable&lt;T&gt;<\/code>.<\/p>\n<p>Type predicates have &quot;if and only if&quot; semantics.\nIf a function returns <code>x is T<\/code>, then it means that:<\/p>\n<ol>\n<li>If the function returns <code>true<\/code> then <code>x<\/code> has the type <code>T<\/code>.<\/li>\n<li>If the function returns <code>false<\/code> then <code>x<\/code> does <em>not<\/em> have type <code>T<\/code>.<\/li>\n<\/ol>\n<p>If you&#8217;re expecting a type predicate to be inferred but it&#8217;s not, then you may be running afoul of the second rule. This often comes up with &quot;truthiness&quot; checks:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>function getClassroomAverage(students: string[], allScores: Map&lt;string, number&gt;) {\r\n  const studentScores = students\r\n    .map(student =&gt; allScores.get(student))\r\n    .filter(score =&gt; !!score);\r\n\r\n  return studentScores.reduce((a, b) =&gt; a + b) \/ studentScores.length;\r\n  \/\/     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n  \/\/ error: Object is possibly 'undefined'.\r\n}\r\n<\/code><\/pre>\n<p>TypeScript did not infer a type predicate for <code>score =&gt; !!score<\/code>, and rightly so: if this returns <code>true<\/code> then <code>score<\/code> is a <code>number<\/code>.\nBut if it returns <code>false<\/code>, then <code>score<\/code> could be either <code>undefined<\/code> or a <code>number<\/code> (specifically, <code>0<\/code>).\nThis is a real bug: if any student got a zero on the test, then filtering out their score will skew the average upwards.\nFewer will be above average and more will be sad!<\/p>\n<p>As with the first example, it&#8217;s better to explicitly filter out <code>undefined<\/code> values:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>function getClassroomAverage(students: string[], allScores: Map&lt;string, number&gt;) {\r\n  const studentScores = students\r\n    .map(student =&gt; allScores.get(student))\r\n    .filter(score =&gt; score !== undefined);\r\n\r\n  return studentScores.reduce((a, b) =&gt; a + b) \/ studentScores.length;  \/\/ ok!\r\n}\r\n<\/code><\/pre>\n<p>A truthiness check <em>will<\/em> infer a type predicate for object types, where there&#8217;s no ambiguity.\nRemember that functions must return a <code>boolean<\/code> to be a candidate for an inferred type predicate: <code>x =&gt; !!x<\/code> might infer a type predicate, but <code>x =&gt; x<\/code> definitely won&#8217;t.<\/p>\n<p>Explicit type predicates continue to work exactly as before.\nTypeScript will not check whether it would infer the same type predicate.\nExplicit type predicates (&quot;is&quot;) are no safer than a type assertion (&quot;as&quot;).<\/p>\n<p>It&#8217;s possible that this feature will break existing code if TypeScript now infers a more precise type than you want. For example:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ Previously, nums: (number | null)[]\r\n\/\/ Now, nums: number[]\r\nconst nums = [1, 2, 3, null, 5].filter(x =&gt; x !== null);\r\n\r\nnums.push(null);  \/\/ ok in TS 5.4, error in TS 5.5\r\n<\/code><\/pre>\n<p>The fix is to tell TypeScript the type that you want using an explicit type annotation:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>const nums: (number | null)[] = [1, 2, 3, null, 5].filter(x =&gt; x !== null);\r\nnums.push(null);  \/\/ ok in all versions\r\n<\/code><\/pre>\n<p>For more information, check out the <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57465\">implementing pull request<\/a> and <a href=\"https:\/\/effectivetypescript.com\/2024\/04\/16\/inferring-a-type-predicate\/\">Dan&#8217;s blog post about implementing this feature<\/a>.<\/p>\n<h2 id=\"control-flow-narrowing-for-constant-indexed-accesses\">Control Flow Narrowing for Constant Indexed Accesses<\/h2>\n<p>TypeScript is now able to narrow expressions of the form <code>obj[key]<\/code> when both <code>obj<\/code> and <code>key<\/code> are effectively constant.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>function f1(obj: Record&lt;string, unknown&gt;, key: string) {\r\n    if (typeof obj[key] === &quot;string&quot;) {\r\n        \/\/ Now okay, previously was error\r\n        obj[key].toUpperCase();\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>In the above, neither <code>obj<\/code> nor <code>key<\/code> are ever mutated, so TypeScript can narrow the type of <code>obj[key]<\/code> to <code>string<\/code> after the <code>typeof<\/code> check.\nFor more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57847\">see the implementing pull request here<\/a>.<\/p>\n<h2 id=\"the-jsdoc-import-tag\">The JSDoc <code>@import<\/code> Tag<\/h2>\n<p>Today, if you want to import something only for type-checking in a JavaScript file, it is cumbersome.\nJavaScript developers can&#8217;t simply import a type named <code>SomeType<\/code> if it&#8217;s not there at runtime.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ .\/some-module.d.ts\r\nexport interface SomeType {\r\n    \/\/ ...\r\n}\r\n\r\n\/\/ .\/index.js\r\nimport { SomeType } from &quot;.\/some-module&quot;; \/\/ \u274c runtime error!\r\n\r\n\/**\r\n * @param {SomeType} myValue\r\n *\/\r\nfunction doSomething(myValue) {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p><code>SomeType<\/code> won&#8217;t exist at runtime, so the import will fail.\nDevelopers can instead use a namespace import instead.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>import * as someModule from &quot;.\/some-module&quot;;\r\n\r\n\/**\r\n * @param {someModule.SomeType} myValue\r\n *\/\r\nfunction doSomething(myValue) {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p>But <code>.\/some-module<\/code> is still imported at runtime &#8211; which might also not be desirable.<\/p>\n<p>To avoid this, developers typically had to use <code>import(...)<\/code> types in JSDoc comments.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/**\r\n * @param {import(&quot;.\/some-module&quot;).SomeType} myValue\r\n *\/\r\nfunction doSomething(myValue) {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p>If you wanted to reuse the same type in multiple places, you could use a <code>typedef<\/code> to avoid repeating the import.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/**\r\n * @typedef {import(&quot;.\/some-module&quot;).SomeType} SomeType\r\n *\/\r\n\r\n\/**\r\n * @param {SomeType} myValue\r\n *\/\r\nfunction doSomething(myValue) {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p>This helps with local uses of <code>SomeType<\/code>, but it gets repetitive for many imports and can be a bit verbose.<\/p>\n<p>That&#8217;s why TypeScript now supports a new <code>@import<\/code> comment tag that has the same syntax as ECMAScript imports.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/** @import { SomeType } from &quot;some-module&quot; *\/\r\n\r\n\/**\r\n * @param {SomeType} myValue\r\n *\/\r\nfunction doSomething(myValue) {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p>Here, we used named imports.\nWe could also have written our import as a namespace import.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/** @import * as someModule from &quot;some-module&quot; *\/\r\n\r\n\/**\r\n * @param {someModule.SomeType} myValue\r\n *\/\r\nfunction doSomething(myValue) {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p>Because these are just JSDoc comments, they don&#8217;t affect runtime behavior at all.<\/p>\n<p>We would like to extend a big thanks to <a href=\"https:\/\/github.com\/a-tarasyuk\">Oleksandr Tarasiuk<\/a> who contributed <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57207\">this change<\/a>!<\/p>\n<h2 id=\"regular-expression-syntax-checking\">Regular Expression Syntax Checking<\/h2>\n<p>Until now, TypeScript has typically skipped over most regular expressions in code.\nThis is because regular expressions technically have an extensible grammar and TypeScript never made any effort to compile regular expressions to earlier versions of JavaScript.\nStill, this meant that lots of common problems would go undiscovered in regular expressions, and they would either turn into errors at runtime, or silently fail.<\/p>\n<p>But TypeScript now does basic syntax checking on regular expressions!<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>let myRegex = \/@robot(\\s+(please|immediately)))? do some task\/;\r\n\/\/                                            ~\r\n\/\/ error!\r\n\/\/ Unexpected ')'. Did you mean to escape it with backslash?\r\n<\/code><\/pre>\n<p>This is a simple example, but this checking can catch a lot of common mistakes.\nIn fact, TypeScript&#8217;s checking goes slightly beyond syntactic checks.\nFor instance, TypeScript can now catch issues around backreferences that don&#8217;t exist.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>let myRegex = \/@typedef \\{import\\((.+)\\)\\.([a-zA-Z_]+)\\} \\3\/u;\r\n\/\/                                                        ~\r\n\/\/ error!\r\n\/\/ This backreference refers to a group that does not exist.\r\n\/\/ There are only 2 capturing groups in this regular expression.\r\n<\/code><\/pre>\n<p>The same applies to named capturing groups.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>let myRegex = \/@typedef \\{import\\((?&lt;importPath&gt;.+)\\)\\.(?&lt;importedEntity&gt;[a-zA-Z_]+)\\} \\k&lt;namedImport&gt;\/;\r\n\/\/                                                                                        ~~~~~~~~~~~\r\n\/\/ error!\r\n\/\/ There is no capturing group named 'namedImport' in this regular expression.\r\n<\/code><\/pre>\n<p>TypeScript&#8217;s checking is now also aware of when certain RegExp features are used when newer than your target version of ECMAScript.\nFor example, if we use named capturing groups like the above in an ES5 target, we&#8217;ll get an error.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>let myRegex = \/@typedef \\{import\\((?&lt;importPath&gt;.+)\\)\\.(?&lt;importedEntity&gt;[a-zA-Z_]+)\\} \\k&lt;importedEntity&gt;\/;\r\n\/\/                                  ~~~~~~~~~~~~         ~~~~~~~~~~~~~~~~\r\n\/\/ error!\r\n\/\/ Named capturing groups are only available when targeting 'ES2018' or later.\r\n<\/code><\/pre>\n<p>The same is true for certain regular expression flags as well.<\/p>\n<p>Note that TypeScript&#8217;s regular expression support is limited to regular expression <em>literals<\/em>.\nIf you try calling <code>new RegExp<\/code> with a string literal, TypeScript will not check the provided string.<\/p>\n<p>We would like to thank <a href=\"https:\/\/github.com\/graphemecluster\/\">GitHub user graphemecluster<\/a> who iterated a ton with us <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/55600\">to get this feature into TypeScript<\/a>.<\/p>\n<h2 id=\"support-for-new-ecmascript-set-methods\">Support for New ECMAScript <code>Set<\/code> Methods<\/h2>\n<p>TypeScript 5.5 declares <a href=\"https:\/\/github.com\/tc39\/proposal-set-methods\">new proposed methods for the ECMAScript <code>Set<\/code> type<\/a>.<\/p>\n<p>Some of these methods, like <code>union<\/code>, <code>intersection<\/code>, <code>difference<\/code>, and <code>symmetricDifference<\/code>, take another <code>Set<\/code> and return a new <code>Set<\/code> as the result.\nThe other methods, <code>isSubsetOf<\/code>, <code>isSupersetOf<\/code>, and <code>isDisjointFrom<\/code>, take another <code>Set<\/code> and return a <code>boolean<\/code>.\nNone of these methods mutate the original <code>Set<\/code>s.<\/p>\n<p>Here&#8217;s a quick example of how you might use these methods and how they behave:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>let fruits = new Set([&quot;apples&quot;, &quot;bananas&quot;, &quot;pears&quot;, &quot;oranges&quot;]);\r\nlet applesAndBananas = new Set([&quot;apples&quot;, &quot;bananas&quot;]);\r\nlet applesAndOranges = new Set([&quot;apples&quot;, &quot;oranges&quot;]);\r\nlet oranges = new Set([&quot;oranges&quot;]);\r\nlet emptySet = new Set();\r\n\r\n\/\/\/\/\r\n\/\/ union\r\n\/\/\/\/\r\n\r\n\/\/ Set(4)\u00a0{'apples', 'bananas', 'pears', 'oranges'}\r\nconsole.log(fruits.union(oranges));\r\n\r\n\/\/ Set(3)\u00a0{'apples', 'bananas', 'oranges'}\r\nconsole.log(applesAndBananas.union(oranges));\r\n\r\n\/\/\/\/\r\n\/\/ intersection\r\n\/\/\/\/\r\n\r\n\/\/ Set(2)\u00a0{'apples', 'bananas'}\r\nconsole.log(fruits.intersection(applesAndBananas));\r\n\r\n\/\/ Set(0)\u00a0{}\r\nconsole.log(applesAndBananas.intersection(oranges));\r\n\r\n\/\/ Set(1)\u00a0{'apples'}\r\nconsole.log(applesAndBananas.intersection(applesAndOranges));\r\n\r\n\/\/\/\/\r\n\/\/ difference\r\n\/\/\/\/\r\n\r\n\/\/ Set(3)\u00a0{'apples', 'bananas', 'pears'}\r\nconsole.log(fruits.difference(oranges));\r\n\r\n\/\/ Set(2)\u00a0{'pears', 'oranges'}\r\nconsole.log(fruits.difference(applesAndBananas));\r\n\r\n\/\/ Set(1) {'bananas'}\r\nconsole.log(applesAndBananas.difference(applesAndOranges));\r\n\r\n\/\/\/\/\r\n\/\/ symmetricDifference\r\n\/\/\/\/\r\n\r\n\/\/ Set(2)\u00a0{'bananas', 'oranges'}\r\nconsole.log(applesAndBananas.symmetricDifference(applesAndOranges)); \/\/ no apples\r\n\r\n\/\/\/\/\r\n\/\/ isDisjointFrom\r\n\/\/\/\/\r\n\r\n\/\/ true\r\nconsole.log(applesAndBananas.isDisjointFrom(oranges));\r\n\r\n\/\/ false\r\nconsole.log(applesAndBananas.isDisjointFrom(applesAndOranges));\r\n\r\n\/\/ true\r\nconsole.log(fruits.isDisjointFrom(emptySet));\r\n\r\n\/\/ true\r\nconsole.log(emptySet.isDisjointFrom(emptySet));\r\n\r\n\/\/\/\/\r\n\/\/ isSubsetOf\r\n\/\/\/\/\r\n\r\n\/\/ true\r\nconsole.log(applesAndBananas.isSubsetOf(fruits));\r\n\r\n\/\/ false\r\nconsole.log(fruits.isSubsetOf(applesAndBananas));\r\n\r\n\/\/ false\r\nconsole.log(applesAndBananas.isSubsetOf(oranges));\r\n\r\n\/\/ true\r\nconsole.log(fruits.isSubsetOf(fruits));\r\n\r\n\/\/ true\r\nconsole.log(emptySet.isSubsetOf(fruits));\r\n\r\n\/\/\/\/\r\n\/\/ isSupersetOf\r\n\/\/\/\/\r\n\r\n\/\/ true\r\nconsole.log(fruits.isSupersetOf(applesAndBananas));\r\n\r\n\/\/ false\r\nconsole.log(applesAndBananas.isSupersetOf(fruits));\r\n\r\n\/\/ false\r\nconsole.log(applesAndBananas.isSupersetOf(oranges));\r\n\r\n\/\/ true\r\nconsole.log(fruits.isSupersetOf(fruits));\r\n\r\n\/\/ false\r\nconsole.log(emptySet.isSupersetOf(fruits));\r\n<\/code><\/pre>\n<p>We&#8217;d like to thank <a href=\"https:\/\/github.com\/bakkot\">Kevin Gibbons<\/a> who not only co-championed the feature in ECMAScript, but <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57230\">also provided the declarations for <code>Set<\/code>, <code>ReadonlySet<\/code>, and <code>ReadonlySetLike<\/code> in TypeScript<\/a>!<\/p>\n<h2 id=\"isolated-declarations\">Isolated Declarations<\/h2>\n<p><em>This section was co-authored by <a href=\"https:\/\/github.com\/robpalme\">Rob Palmer<\/a> who supported the design of isolated declarations.<\/em><\/p>\n<p>Declaration files (a.k.a. <code>.d.ts<\/code> files) describe the shape of existing libraries and modules to TypeScript.\nThis lightweight description includes the library&#8217;s type signatures and excludes implementation details such as the function bodies.\nThey are published so that TypeScript can efficiently check your usage of a library without needing to analyse the library itself.\nWhilst it is possible to handwrite declaration files, if you are authoring typed code, it&#8217;s much safer and simpler to let TypeScript generate them automatically from source files using <code>--declaration<\/code>.<\/p>\n<p>The TypeScript compiler and its APIs have always had the job of generating declaration files;\nhowever, there are some use-cases where you might want to use other tools, or where the traditional build process doesn&#8217;t scale.<\/p>\n<h3 id=\"use-case-faster-declaration-emit-tools\">Use-case: Faster Declaration Emit Tools<\/h3>\n<p>Imagine if you wanted to create a faster tool to generate declaration files, perhaps as part of a publishing service or a new bundler.\nWhilst there is a thriving ecosystem of blazing fast tools that can turn TypeScript into JavaScript, the same is not true for turning TypeScript into declaration files.\nThe reason is that TypeScript&#8217;s inference allows us to write code without explicitly declaring types, meaning declaration emit can be complex.<\/p>\n<p>Let&#8217;s consider a simple example of a function that adds two imported variables.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ util.ts\r\nexport let one = &quot;1&quot;;\r\nexport let two = &quot;2&quot;;\r\n\r\n\/\/ add.ts\r\nimport { one, two } from &quot;.\/util&quot;;\r\nexport function add() { return one + two; }\r\n<\/code><\/pre>\n<p>Even if the only thing we want to do is generate <code>add.d.ts<\/code>, TypeScript needs to crawl into another imported file (<code>util.ts<\/code>), infer that the type of <code>one<\/code> and <code>two<\/code> are strings, and then calculate that the <code>+<\/code> operator on two strings will lead to a <code>string<\/code> return type.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ add.d.ts\r\nexport declare function add(): string;\r\n<\/code><\/pre>\n<p>While this inference is important for the developer experience, it means that tools that want to generate declaration files would need to replicate parts of the type-checker including inference and the ability to resolve module specifiers to follow the imports.<\/p>\n<h3 id=\"use-case-parallel-declaration-emit-and-parallel-checking\">Use-case: Parallel Declaration Emit and Parallel Checking<\/h3>\n<p>Imagine if you had a monorepo containing many projects and a multi-core CPU that just wished it could help you check your code faster.\nWouldn&#8217;t it be great if we could check all those projects at the same time by running each project on a different core?<\/p>\n<p>Unfortunately we don&#8217;t have the freedom to do all the work in parallel.\nThe reason is that we have to build those projects in dependency order, because each project is checking against the declaration files of their dependencies.\nSo we must build the dependency first to generate the declaration files.\nTypeScript&#8217;s project references feature works the same way, building the set of projects in &quot;topological&quot; dependency order.<\/p>\n<p>As an example, if we have two projects called <code>backend<\/code> and <code>frontend<\/code>, and they both depend on a project called <code>core<\/code>, TypeScript can&#8217;t start type-checking either <code>frontend<\/code> or <code>backend<\/code> until <code>core<\/code> has been built and its declaration files have been generated.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/typescript\/wp-content\/uploads\/sites\/11\/2024\/04\/5-5-beta-isolated-declarations-deps.png\" alt=\"frontend and backend point to core, other stuff might point to each of those\"><\/p>\n<p>In the above graph, you can see that we have a bottleneck.\nWhilst we can build <code>frontend<\/code> and <code>backend<\/code> in parallel, we need to first wait for <code>core<\/code> to finish building before either can start.<\/p>\n<p>How could we improve upon this?\nWell, if a fast tool could generate all those declaration files for <code>core<\/code> <em>in parallel<\/em>, TypeScript then could immediately follow that by type-checking <code>core<\/code>, <code>frontend<\/code>, and <code>backend<\/code> also <em>in parallel<\/em>.<\/p>\n<h3 id=\"solution-explicit-types\">Solution: Explicit Types!<\/h3>\n<p>The common requirement in both use-cases is that we need a cross-file type-checker to generate declaration files.\nWhich is a lot to ask from the tooling community.<\/p>\n<p>As a more complex example, if we want a declaration file for the following code&#8230;<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>import { add } from &quot;.\/add&quot;;\r\n\r\nconst x = add();\r\n\r\nexport function foo() {\r\n    return x;\r\n}\r\n<\/code><\/pre>\n<p>&#8230;we would need to generate a signature for <code>foo<\/code>.\nWell that requires looking at the implementation of <code>foo<\/code>.\n<code>foo<\/code> just returns <code>x<\/code>, so getting the type of <code>x<\/code>  requires looking at the implementation of <code>add<\/code>.\nBut that might require looking at the implementation of <code>add<\/code>&#8216;s dependencies, and so on.\nWhat we&#8217;re seeing here is that generating declaration files requires a whole lot of logic to figure out the types of different places that might not even be local to the current file.<\/p>\n<p>Still, for developers looking for fast iteration time and fully parallel builds, there is another way of thinking about this problem.\nA declaration file only requires the types of the public API of a module &#8211; in other words, the types of the things that are exported.\nIf, controversially, developers are willing to explicitly write out the types of the things they export, tools could generate declaration files without needing to look at the implementation of the module &#8211; and without reimplementing a full type-checker.<\/p>\n<p>This is where the new <code>--isolatedDeclarations<\/code> option comes in.\n<code>--isolatedDeclarations<\/code> reports errors when a module can&#8217;t be reliably transformed without a type-checker.\nMore plainly, it makes TypeScript report errors if you have a file that isn&#8217;t sufficiently annotated on its exports.<\/p>\n<p>That means in the above example, we would see an error like the following:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>export function foo() {\r\n\/\/              ~~~\r\n\/\/ error! Function must have an explicit\r\n\/\/ return type annotation with --isolatedDeclarations.\r\n    return x;\r\n}\r\n<\/code><\/pre>\n<h3 id=\"why-are-errors-desirable\">Why are errors desirable?<\/h3>\n<p>Because it means that TypeScript can<\/p>\n<ol>\n<li>Tell us up-front whether other tools will have issues with generating declaration files<\/li>\n<li>Provide a quick fix to help add these missing annotations.<\/li>\n<\/ol>\n<p>This mode doesn&#8217;t require annotations <em>everywhere<\/em> though.\nFor locals, these can be ignored, since they don&#8217;t affect the public API.\nFor example, the following code would <strong>not<\/strong> produce an error:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>import { add } from &quot;.\/add&quot;;\r\n\r\nconst x = add(&quot;1&quot;, &quot;2&quot;); \/\/ no error on 'x', it's not exported.\r\n\r\nexport function foo(): string {\r\n    return x;\r\n}\r\n<\/code><\/pre>\n<p>There are also certain expressions where the type is &quot;trivial&quot; to calculate.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ No error on 'x'.\r\n\/\/ It's trivial to calculate the type is 'number'\r\nexport let x = 10;\r\n\r\n\/\/ No error on 'y'.\r\n\/\/ We can get the type from the return expression.\r\nexport function y() {\r\n    return 20;\r\n}\r\n\r\n\/\/ No error on 'z'.\r\n\/\/ The type assertion makes it clear what the type is.\r\nexport function z() {\r\n    return Math.max(x, y()) as number;\r\n}\r\n<\/code><\/pre>\n<h3 id=\"using-isolateddeclarations\">Using <code>isolatedDeclarations<\/code><\/h3>\n<p><code>isolatedDeclarations<\/code> requires that either the <code>declaration<\/code> or <code>composite<\/code> flags are also set.<\/p>\n<p>Note that <code>isolatedDeclarations<\/code> does not change how TypeScript performs emit &#8211; just how it reports errors.\nImportantly, and similar to <code>isolatedModules<\/code>, enabling the feature in TypeScript won&#8217;t immediately bring about the potential benefits discussed here.\nSo please be patient and look forward to future developments in this space.\nKeeping tool authors in mind, we should also recognize that today, not all of TypeScript&#8217;s declaration emit can be easily replicated by other tools wanting to use it as a guide.\nThat&#8217;s something we&#8217;re actively working on improving.<\/p>\n<p>On top of this, isolated declarations are still a new feature, and we&#8217;re actively working on improving the experience.\nSome scenarios, like using computed property declarations in classes and object literals, are not <em>yet<\/em> supported under <code>isolatedDeclarations<\/code>.\nKeep an eye on this space, and feel free to provide us with feedback.<\/p>\n<p>We also feel it is worth calling out that <code>isolatedDeclarations<\/code> should be adopted on a case-by-case basis.\nThere are some developer ergonomics that are lost when using <code>isolatedDeclarations<\/code>, and thus it may not be the right choice if your setup is not leveraging the two scenarios mentioned earlier.\nFor others, the work on <code>isolatedDeclarations<\/code> has already uncovered many optimizations and opportunities to unlock different parallel build strategies.\nIn the meantime, if you&#8217;re willing to make the trade-offs, we believe <code>isolatedDeclarations<\/code> can be a powerful tool to speed up your build process as external tooling becomes more widely available.<\/p>\n<p>For more information, read up on the <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/58944\">Isolated Declarations: State of the Feature<\/a> discussion on the TypeScript issue tracker.<\/p>\n<h3 id=\"credit\">Credit<\/h3>\n<p>Work on <code>isolatedDeclarations<\/code> has been a long-time collaborative effort between the TypeScript team and the infrastructure and tooling teams within Bloomberg and Google.\nIndividuals like Hana Joo from Google who implemented <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58260\">the quick fix for isolated declaration errors<\/a> (more on that soon), as well as Ashley Claymore, Jan K\u00fchle, Lisa Velden, Rob Palmer, and Thomas Chetwin have been involved in discussion, specification, and implementation for many months.\nBut we feel it is specifically worth calling out the tremendous amount of work provided by <a href=\"https:\/\/github.com\/dragomirtitian\">Titian Cernicova-Dragomir<\/a> from Bloomberg.\nTitian has been instrumental in driving the implementation of <code>isolatedDeclarations<\/code> and has been a contributor to the TypeScript project for years prior.<\/p>\n<p>While the feature involved many changes, you can see <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58201\">the core work for Isolated Declarations here<\/a>.<\/p>\n<h2 id=\"the-configdir-template-variable-for-configuration-files\">The <code>${configDir}<\/code> Template Variable for Configuration Files<\/h2>\n<p>It&#8217;s common in many codebases to reuse a shared <code>tsconfig.json<\/code> file that acts as a &quot;base&quot; for other configuration files.\nThis is done by using the <code>extends<\/code> field in a <code>tsconfig.json<\/code> file.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>{\r\n    &quot;extends&quot;: &quot;..\/..\/tsconfig.base.json&quot;,\r\n    &quot;compilerOptions&quot;: {\r\n        &quot;outDir&quot;: &quot;.\/dist&quot;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>One of the issues with this is that all paths in the <code>tsconfig.json<\/code> file are relative to the location of the file itself.\nThis means that if you have a shared <code>tsconfig.base.json<\/code> file that is used by multiple projects, relative paths often won&#8217;t be useful in the derived projects.\nFor example, imagine the following <code>tsconfig.base.json<\/code>:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>{\r\n    &quot;compilerOptions&quot;: {\r\n        &quot;typeRoots&quot;: [\r\n            &quot;.\/node_modules\/@types&quot;\r\n            &quot;.\/custom-types&quot;\r\n        ],\r\n        &quot;outDir&quot;: &quot;dist&quot;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>If author&#8217;s intent was that every <code>tsconfig.json<\/code> that extends this file should<\/p>\n<ol>\n<li>output to a <code>dist<\/code> directory relative to the derived <code>tsconfig.json<\/code> , and<\/li>\n<li>have a <code>custom-types<\/code> directory relative to the derived <code>tsconfig.json<\/code>,<\/li>\n<\/ol>\n<p>then this would not work.\nThe <code>typeRoots<\/code> paths would be relative to the location of the shared <code>tsconfig.base.json<\/code> file, not the project that extends it.\nEach project that extends this shared file would need to declare its own <code>outDir<\/code> and <code>typeRoots<\/code> with identical contents.\nThis could be frustrating and hard to keep in sync between projects, and while the example above is using <code>typeRoots<\/code>, this is a common problem for <code>paths<\/code> and other options.<\/p>\n<p>To solve this, TypeScript 5.5 introduces a new template variable <code>${configDir}<\/code>.\nWhen <code>${configDir}<\/code> is written in certain path fields of a <code>tsconfig.json<\/code> or <code>jsconfig.json<\/code> files, this variable is substituted with the containing directory of the configuration file in a given compilation.\nThis means that the above <code>tsconfig.base.json<\/code> could be rewritten as:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>{\r\n    &quot;compilerOptions&quot;: {\r\n        &quot;typeRoots&quot;: [\r\n            &quot;${configDir}\/node_modules\/@types&quot;\r\n            &quot;${configDir}\/custom-types&quot;\r\n        ],\r\n        &quot;outDir&quot;: &quot;${configDir}\/dist&quot;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Now, when a project extends this file, the paths will be relative to the derived <code>tsconfig.json<\/code>, not the shared <code>tsconfig.base.json<\/code> file.\nThis makes it easier to share configuration files across projects and ensures that the configuration files are more portable.<\/p>\n<p>If you intend to make a <code>tsconfig.json<\/code> file extendable, consider if a <code>.\/<\/code> should instead be written with <code>${configDir}<\/code>.<\/p>\n<p>For more information, see <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/57485\">the proposal issue<\/a> and <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58042\">the implementing pull request<\/a>.<\/p>\n<h2 id=\"consulting-packagejson-dependencies-for-declaration-file-generation\">Consulting <code>package.json<\/code> Dependencies for Declaration File Generation<\/h2>\n<p>Previously, TypeScript would often issue an error message like<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>The inferred type of &quot;X&quot; cannot be named without a reference to &quot;Y&quot;. This is likely not portable. A type annotation is necessary.\r\n<\/code><\/pre>\n<p>This was often due to TypeScript&#8217;s declaration file generation finding itself in the contents of files that were never explicitly imported in a program.\nGenerating an import to such a file could be risky if the path ended up being relative.\nStill, for codebases with explicit dependencies in the <code>dependencies<\/code> (or <code>peerDependencies<\/code> and <code>optionalDependencies<\/code>) of a <code>package.json<\/code>, generating such an import should be safe under certain resolution modes.\nSo in TypeScript 5.5, we&#8217;re more lenient when that&#8217;s the case, and many occurrences of this error should disappear.<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/42873\">See this pull request<\/a> for more details on the change.<\/p>\n<h2 id=\"editor-and-watch-mode-reliability-improvements\">Editor and Watch-Mode Reliability Improvements<\/h2>\n<p>TypeScript has either added some new functionality or fixed existing logic that makes <code>--watch<\/code> mode and TypeScript&#8217;s editor integration feel more reliable.\nThat should hopefully translate to fewer TSServer\/editor restarts.<\/p>\n<h3 id=\"correctly-refresh-editor-errors-in-configuration-files\">Correctly Refresh Editor Errors in Configuration Files<\/h3>\n<p>TypeScript can generate errors for <code>tsconfig.json<\/code> files;\nhowever, those errors are actually generated from loading a project, and editors typically don&#8217;t directly request those errors for <code>tsconfig.json<\/code> files.\nWhile this sounds like a technical detail, it means that when all errors issued in a <code>tsconfig.json<\/code> are fixed, TypeScript doesn&#8217;t issue a new fresh empty set of errors, and users are left with stale errors unless they reload their editor.<\/p>\n<p>TypeScript 5.5 now intentionally issues an event to clear these out.\n<a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58120\">See more here<\/a>.<\/p>\n<h3 id=\"better-handling-for-deletes-followed-by-immediate-writes\">Better Handling for Deletes Followed by Immediate Writes<\/h3>\n<p>Instead of overwriting files, some tools will opt to delete them and then create new files from scratch.\nThis is the case when running <code>npm ci<\/code>, for instance.<\/p>\n<p>While this can be efficient for those tools, it can be problematic for TypeScript&#8217;s editor scenarios where deleting a watched might dispose of it and all of its transitive dependencies.\nDeleting and creating a file in quick succession could lead to TypeScript tearing down an entire project and then rebuilding it from scratch.<\/p>\n<p>TypeScript 5.5 now has a more nuanced approach by keeping parts of a deleted project around until it picks up on a new creation event.\nThis should make operations like <code>npm ci<\/code> work a lot better with TypeScript.\nSee <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57492\">more information on the approach here<\/a>.<\/p>\n<h3 id=\"symlinks-are-tracked-in-failed-resolutions\">Symlinks are Tracked in Failed Resolutions<\/h3>\n<p>When TypeScript fails to resolve a module, it will still need to watch for any failed lookup paths in case the module is added later.\nPreviously this was not done for symlinked directories, which could cause reliability issues in monorepo-like scenarios when a build occurred in one project but was not witnessed in the other.\nThis should be fixed in TypeScript 5.5, and means you won&#8217;t need to restart your editor as often.<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58139\">See more information here<\/a>.<\/p>\n<h3 id=\"project-references-contribute-to-auto-imports\">Project References Contribute to Auto-Imports<\/h3>\n<p>Auto-imports no longer requires at least one explicit import to dependent projects in a project reference setup.\nInstead, auto-import completions should just work across anything you&#8217;ve listed in the <code>references<\/code> field of your <code>tsconfig.json<\/code>.<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/55955\">See more on the implementing pull request<\/a>.<\/p>\n<h2 id=\"performance-and-size-optimizations\">Performance and Size Optimizations<\/h2>\n<h3 id=\"monomorphized-objects-in-language-service-and-public-api\">Monomorphized Objects in Language Service and Public API<\/h3>\n<p>In TypeScript 5.0, we ensured that our <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/51682\"><code>Node<\/code><\/a> and <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/51880\"><code>Symbol<\/code><\/a> objects had a consistent set of properties with a consistent initialization order.\nDoing so helps reduce polymorphism in different operations, which allows runtimes to fetch properties more quickly.<\/p>\n<p>By making this change, we witnessed impressive speed wins in the compiler;\nhowever, most of these changes were performed on internal allocators for our data structures.\nThe language service, along with TypeScript&#8217;s public API, uses a different set of allocators for certain objects.\nThis allowed the TypeScript compiler to be a bit leaner, as data used only for the language service would never be used in the compiler.<\/p>\n<p>In TypeScript 5.5, the same monomorphization work has been done for the language service and public API.\nWhat this means is that your editor experience, and any build tools that use the TypeScript API, will get a decent amount faster.\nIn fact, in our benchmarks, we&#8217;ve seen a <strong>5-8% speedup in build times<\/strong> when using the public TypeScript API&#8217;s allocators, and <strong>language service operations getting 10-20% faster<\/strong>.\nWhile this does imply an increase in memory, we believe that tradeoff is worth it and hope to find ways to reduce that memory overhead.\nThings should feel a lot snappier now.<\/p>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58045\">see the change here<\/a>.<\/p>\n<h3 id=\"monomorphized-control-flow-nodes\">Monomorphized Control Flow Nodes<\/h3>\n<p>In TypeScript 5.5, nodes of the control flow graph have been monomorphized so that they always hold a consistent shape.\nBy doing so, check times will often be reduced by about 1%.<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57977\">See this change here<\/a>.<\/p>\n<h3 id=\"optimizations-on-our-control-flow-graph\">Optimizations on our Control Flow Graph<\/h3>\n<p>In many cases, control flow analysis will traverse nodes that don&#8217;t provide any new information.\nWe observed that in the absence of any early termination or effects in the antecedents (or &quot;dominators&quot;) of certain nodes meant that those nodes could always be skipped over.\nAs such, TypeScript now constructs its control flow graphs to take advantage of this by linking to an earlier node that <em>does<\/em> provide interesting information for control flow analysis.\nThis yields a flatter control flow graph, which can be more efficient to traverse.\nThis optimization has yielded modest gains, but with up to 2% reductions in build time on certain codebases.<\/p>\n<p>You can <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58013\">read more here<\/a>.<\/p>\n<h3 id=\"skipped-checking-in-transpilemodule-and-transpiledeclaration\">Skipped Checking in <code>transpileModule<\/code> and <code>transpileDeclaration<\/code><\/h3>\n<p>TypeScript&#8217;s <code>transpileModule<\/code> API can be used for compiling a single TypeScript file&#8217;s contents into JavaScript.\nSimilarly, the <code>transpileDeclaration<\/code> API (see below) can be used to generate a declaration file for a single TypeScript file.\nOne of the issues with these APIs is that TypeScript internally would perform a full type-checking pass over the entire contents of the file before emitting the output.\nThis was necessary to collect certain information which would later be used for the emit phase.<\/p>\n<p>In TypeScript 5.5, we&#8217;ve found a way to avoid performing a full check, only lazily collecting this information as necessary, and <code>transpileModule<\/code> and <code>transpileDeclaration<\/code> both enable this functionality by default.\nAs a result, tools that integrate with these APIs, like <a href=\"https:\/\/www.npmjs.com\/package\/ts-loader\">ts-loader<\/a> with <code>transpileOnly<\/code> and <a href=\"https:\/\/www.npmjs.com\/package\/ts-jest\">ts-jest<\/a>, should see a noticeable speedup.\nIn our testing, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58364#issuecomment-2138580690\">we generally witness around a 2x speed-up in build time using <code>transpileModule<\/code><\/a>.<\/p>\n<h3 id=\"typescript-package-size-reduction\">TypeScript Package Size Reduction<\/h3>\n<p>Further leveraging <a href=\"https:\/\/devblogs.microsoft.com\/typescript\/typescripts-migration-to-modules\/\">our transition to modules in 5.0<\/a>, we&#8217;ve significantly reduced TypeScript&#8217;s overall package size <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/55326\">by making <code>tsserver.js<\/code> and <code>typingsInstaller.js<\/code> import from a common API library instead of having each of them produce standalone bundles<\/a>.<\/p>\n<p>This reduces TypeScript&#8217;s size on disk from 30.2 MB to 20.4 MB, and reduces its packed size from 5.5 MB to 3.7 MB!<\/p>\n<h3 id=\"node-reuse-in-declaration-emit\">Node Reuse in Declaration Emit<\/h3>\n<p>As part of the work to enable <code>isolatedDeclarations<\/code>, we&#8217;ve substantially improved how often TypeScript can directly copy your input source code when producing declaration files.<\/p>\n<p>For example, let&#8217;s say you wrote<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>export const strBool: string | boolean = &quot;hello&quot;;\r\nexport const boolStr: boolean | string = &quot;world&quot;;\r\n<\/code><\/pre>\n<p>Note that the union types are equivalent, but the order of the union is different.\nWhen emitting the declaration file, TypeScript has two equivalent output possibilities.<\/p>\n<p>The first is to use a consistent canonical representation for each type:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>export const strBool: string | boolean;\r\nexport const boolStr: string | boolean;\r\n<\/code><\/pre>\n<p>The second is to re-use the type annotations exactly as written:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>export const strBool: string | boolean;\r\nexport const boolStr: boolean | string;\r\n<\/code><\/pre>\n<p>The second approach is generally preferable for a few reasons:<\/p>\n<ul>\n<li>Many equivalent representations still encode some level of intent that is better to preserve in the declaration file<\/li>\n<li>Producing a fresh representation of a type can be somewhat expensive, so avoiding is better<\/li>\n<li>User-written types are usually shorter than generated type representations<\/li>\n<\/ul>\n<p>In 5.5, we&#8217;ve greatly improved the number of places where TypeScript can correctly identify places where it&#8217;s safe and correct to print back types exactly as they were written in the input file.\nMany of these cases are invisible performance improvements &#8211; TypeScript would generate fresh sets of syntax nodes and serialize them into a string.\nInstead, TypeScript can now operate over the original syntax nodes directly, which is much cheaper and faster.<\/p>\n<h3 id=\"caching-contextual-types-from-discriminated-unions\">Caching Contextual Types from Discriminated Unions<\/h3>\n<p>When TypeScript asks for the contextual type of an expression like an object literal, it will often encounter a union type.\nIn those cases, TypeScript tries to filter out members of the union based on known properties with well known values (i.e. discriminant properties).\nThis work can be fairly expensive, especially if you end up with an object consisting of many many properties.\nIn TypeScript 5.5, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58372\">much of the computation is cached once so that TypeScript doesn&#8217;t need to recompute it for every property in the object literal<\/a>.\nPerforming this optimization shaved 250ms off of compiling the TypeScript compiler itself.<\/p>\n<h2 id=\"easier-api-consumption-from-ecmascript-modules\">Easier API Consumption from ECMAScript Modules<\/h2>\n<p>Previously, if you were writing an ECMAScript module in Node.js, named imports were not available from the <code>typescript<\/code> package.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>import { createSourceFile } from &quot;typescript&quot;; \/\/ \u274c error\r\n\r\nimport * as ts from &quot;typescript&quot;;\r\nts.createSourceFile \/\/ \u274c undefined???\r\n\r\nts.default.createSourceFile \/\/ \u2705 works - but ugh!\r\n<\/code><\/pre>\n<p>This is because <a href=\"https:\/\/github.com\/nodejs\/cjs-module-lexer\">cjs-module-lexer<\/a> did not recognize the pattern of TypeScript&#8217;s generated CommonJS code.\nThis has been fixed, and users can now use named imports from the TypeScript npm package with ECMAScript modules in Node.js.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>import { createSourceFile } from &quot;typescript&quot;; \/\/ \u2705 works now!\r\n\r\nimport * as ts from &quot;typescript&quot;;\r\nts.createSourceFile \/\/ \u2705 works now!\r\n<\/code><\/pre>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57133\">see the change here<\/a>.<\/p>\n<h2 id=\"the-transpiledeclaration-api\">The <code>transpileDeclaration<\/code> API<\/h2>\n<p>TypeScript&#8217;s API exposes a function called <code>transpileModule<\/code>.\nIt&#8217;s intended to make it easy to compile a single file of TypeScript code.\nBecause it doesn&#8217;t have access to an entire <em>program<\/em>, the caveat is that it may not produce the right output if the code violates any errors under the <code>isolatedModules<\/code> option.<\/p>\n<p>In TypeScript 5.5, we&#8217;ve added a new similar API called <code>transpileDeclaration<\/code>.\nThis API is similar to <code>transpileModule<\/code>, but it&#8217;s specifically designed to generate a single <em>declaration file<\/em> based on some input source text.\nJust like <code>transpileModule<\/code>, it doesn&#8217;t have access to a full program, and a similar caveat applies: it only generates an accurate declaration file if the input code is free of errors under the new <code>isolatedDeclarations<\/code> option.<\/p>\n<p>If desired, this function can be used to parallelize declaration emit across all files under <code>isolatedDeclarations<\/code> mode.<\/p>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58261\">see the implementation here<\/a>.<\/p>\n<h2 id=\"notable-behavioral-changes\">Notable Behavioral Changes<\/h2>\n<p>This section highlights a set of noteworthy changes that should be acknowledged and understood as part of any upgrade.\nSometimes it will highlight deprecations, removals, and new restrictions.\nIt can also contain bug fixes that are functionally improvements, but which can also affect an existing build by introducing new errors.<\/p>\n<h3 id=\"disabling-features-deprecated-in-typescript-50\">Disabling Features Deprecated in TypeScript 5.0<\/h3>\n<p>TypeScript 5.0 deprecated the following options and behaviors:<\/p>\n<ul>\n<li><code>charset<\/code><\/li>\n<li><code>target: ES3<\/code><\/li>\n<li><code>importsNotUsedAsValues<\/code><\/li>\n<li><code>noImplicitUseStrict<\/code><\/li>\n<li><code>noStrictGenericChecks<\/code><\/li>\n<li><code>keyofStringsOnly<\/code><\/li>\n<li><code>suppressExcessPropertyErrors<\/code><\/li>\n<li><code>suppressImplicitAnyIndexErrors<\/code><\/li>\n<li><code>out<\/code><\/li>\n<li><code>preserveValueImports<\/code><\/li>\n<li><code>prepend<\/code> in project references<\/li>\n<li>implicitly OS-specific <code>newLine<\/code><\/li>\n<\/ul>\n<p>To continue using the deprecated options above, developers using TypeScript 5.0 and other more recent versions have had to specify a new option called <code>ignoreDeprecations<\/code> with the value <code>&quot;5.0&quot;<\/code>.<\/p>\n<p>In TypeScript 5.5, these options no longer have any effect.\nTo help with a smooth upgrade path, you may still specify them in your tsconfig, but these will be an error to specify in TypeScript 6.0.\nSee also the <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/51000\">Flag Deprecation Plan<\/a> which outlines our deprecation strategy.<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/51909\">More information around these deprecation plans is available on GitHub<\/a>, which contains suggestions in how to best adapt your codebase.<\/p>\n<h3 id=\"libdts-changes\"><code>lib.d.ts<\/code> Changes<\/h3>\n<p>Types generated for the DOM may have an impact on type-checking your codebase.\nFor more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/58211\">see the DOM updates for TypeScript 5.5<\/a>.<\/p>\n<h3 id=\"stricter-parsing-for-decorators\">Stricter Parsing for Decorators<\/h3>\n<p>Since TypeScript originally introduced support for decorators, the specified grammar for the proposal has been tightened up.\nTypeScript is now stricter about what forms it allows.\nWhile rare, existing decorators may need to be parenthesized to avoid errors.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>class DecoratorProvider {\r\n    decorate(...args: any[]) { }\r\n}\r\n\r\nclass D extends DecoratorProvider {\r\n    m() {\r\n        class C {\r\n            @super.decorate \/\/ \u274c error\r\n            method1() { }\r\n\r\n            @(super.decorate) \/\/ \u2705 okay\r\n            method2() { }\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>See <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57749\">more information on the change here<\/a>.<\/p>\n<h3 id=\"undefined-is-no-longer-a-definable-type-name\"><code>undefined<\/code> is No Longer a Definable Type Name<\/h3>\n<p>TypeScript has always disallowed type alias names that conflict with built-in types:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ Illegal\r\ntype null = any;\r\n\/\/ Illegal\r\ntype number = any;\r\n\/\/ Illegal\r\ntype object = any;\r\n\/\/ Illegal\r\ntype any = any;\r\n<\/code><\/pre>\n<p>Due to a bug, this logic didn&#8217;t also apply to the built-in type <code>undefined<\/code>.\nIn 5.5, this is now correctly identified as an error:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ Now also illegal\r\ntype undefined = any;\r\n<\/code><\/pre>\n<p>Bare references to type aliases named <code>undefined<\/code> never actually worked in the first place.\nYou could define them, but you couldn&#8217;t use them as an unqualified type name.<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>export type undefined = string;\r\nexport const m: undefined = &quot;&quot;;\r\n\/\/           ^\r\n\/\/ Errors in 5.4 and earlier - the local definition of 'undefined' was not even consulted.\r\n<\/code><\/pre>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57575\">see the change here<\/a>.<\/p>\n<h3 id=\"simplified-reference-directive-declaration-emit\">Simplified Reference Directive Declaration Emit<\/h3>\n<p>When producing a declaration file, TypeScript would synthesize a reference directive when it believed one was required.\nFor example, all Node.js modules are declared ambiently, so cannot be loaded by module resolution alone.\nA file like:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>import path from &quot;path&quot;;\r\nexport const myPath = path.parse(__filename);\r\n<\/code><\/pre>\n<p>Would emit a declaration file like:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/\/ &lt;reference types=&quot;node&quot; \/&gt;\r\nimport path from &quot;path&quot;;\r\nexport declare const myPath: path.ParsedPath;\r\n<\/code><\/pre>\n<p>Even though the reference directive never appeared in the original source.<\/p>\n<p>Similarly, TypeScript also <em>removed<\/em> reference directives that it did not believe needed to be a part of the output.\nFor example, let&#8217;s imagine we had a reference directive to <code>jest<\/code>;\nhowever, imagine the reference directive isn&#8217;t necessary to generate the declaration file.\nTypeScript would simply drop it.\nSo in the following example:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/\/ &lt;reference types=&quot;jest&quot; \/&gt;\r\nimport path from &quot;path&quot;;\r\nexport const myPath = path.parse(__filename);\r\n<\/code><\/pre>\n<p>TypeScript would still emit:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/\/ &lt;reference types=&quot;node&quot; \/&gt;\r\nimport path from &quot;path&quot;;\r\nexport declare const myPath: path.ParsedPath;\r\n<\/code><\/pre>\n<p>In the course of working on <code>isolatedDeclarations<\/code>, we realized that this logic was untenable for anyone attempting to implement a declaration emitter without type checking or using more than a single file&#8217;s context.\nThis behavior is also hard to understand from a user&#8217;s perspective; whether or not a reference directive appeared in the emitted file seems inconsistent and difficult to predict unless you understand exactly what&#8217;s going on during typechecking.\nTo prevent declaration emit from being different when <code>isolatedDeclarations<\/code> was enabled, we knew that our emit needed to change.<\/p>\n<p>Through <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57569\">experimentation<\/a>, we found that nearly all cases where TypeScript synthesized reference directives were just to pull in <code>node<\/code> or <code>react<\/code>.\nThese are cases where the expectation is that a downstream user already references those types through tsconfig.json <code>&quot;types&quot;<\/code> or library imports, so no longer synthesizing these reference directives would be unlikely to break anyone.\nIt&#8217;s worth noting that this is already how it works for <code>lib.d.ts<\/code>; TypeScript doesn&#8217;t synthesize a reference to <code>lib=&quot;es2015&quot;<\/code> when a module exports a <code>WeakMap<\/code>, instead assuming that a downstream user will have included that as part of their environment.<\/p>\n<p>For reference directives that had been written by library authors (not synthesized), <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/57656\">further experimentation<\/a> showed that nearly all were removed, never showing up in the output.\nMost reference directives that were preserved were broken and likely not intended to be preserved.<\/p>\n<p>Given those results, we decided to greatly simplfy reference directives in declaration emit in TypeScript 5.5.\nA more consistent strategy will help library authors and consumers have better control of their declaration files.<\/p>\n<p>Reference directives are no longer synthesized.\nUser-written reference directives are no longer preserved, unless annotated with a new <code>preserve=&quot;true&quot;<\/code> attribute.\nConcretely, an input file like:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/\/ &lt;reference types=&quot;some-lib&quot; preserve=&quot;true&quot; \/&gt;\r\n\/\/\/ &lt;reference types=&quot;jest&quot; \/&gt;\r\nimport path from &quot;path&quot;;\r\nexport const myPath = path.parse(__filename);\r\n<\/code><\/pre>\n<p>will emit:<\/p>\n<pre class=\"lang:default decode:true\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/\/ &lt;reference types=&quot;some-lib&quot; preserve=&quot;true&quot; \/&gt;\r\nimport path from &quot;path&quot;;\r\nexport declare const myPath: path.ParsedPath;\r\n<\/code><\/pre>\n<p>Adding <code>preserve=&quot;true&quot;<\/code> is backwards compatible with older versions of TypeScript as unknown attributes are ignored.<\/p>\n<p>This change also improved performance; in our benchmarks, the emit stage saw a 1-4% improvement in projects with declaration emit enabled.<\/p>\n<h2 id=\"whats-next\">What&#8217;s Next?<\/h2>\n<p>The next version of TypeScript will be TypeScript 5.6, and we&#8217;re looking at a release in early September of 2024 (a few months from now).\nYou can try out early versions of TypeScript 5.6 today by installing our <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/nightly-builds.html\">nightly builds<\/a> from npm with <code>npm install _D typescript@next<\/code>, or by using the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-vscode.vscode-typescript-next\">VS Code TypeScript Nightly<\/a> extension.\nKeep an eye out for the upcoming iteration plan <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\">on our issue tracker<\/a>, which will have precise target dates and features we&#8217;re focused on.<\/p>\n<p>Until then, TypeScript 5.5 is here and ready to use, and we&#8217;re excited to for you to install it today!\nWe hope that this release makes your day-to-day coding a joy.<\/p>\n<p>Happy Hacking!<\/p>\n<p>&#8211; Daniel Rosenwasser and the TypeScript Team<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today we&#8217;re excited to announce the release of TypeScript 5.5! If you&#8217;re not familiar with TypeScript, it&#8217;s a language that builds on top of JavaScript by making it possible to declare and describe types. Writing types in our code allows us to explain intent and have other tools check our code to catch mistakes like [&hellip;]<\/p>\n","protected":false},"author":381,"featured_media":1797,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4270","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-typescript"],"acf":[],"blog_post_summary":"<p>Today we&#8217;re excited to announce the release of TypeScript 5.5! If you&#8217;re not familiar with TypeScript, it&#8217;s a language that builds on top of JavaScript by making it possible to declare and describe types. Writing types in our code allows us to explain intent and have other tools check our code to catch mistakes like [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/posts\/4270","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/users\/381"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/comments?post=4270"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/posts\/4270\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/media\/1797"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/media?parent=4270"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/categories?post=4270"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/tags?post=4270"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}