{"id":4607,"date":"2025-01-29T10:51:38","date_gmt":"2025-01-29T18:51:38","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/typescript\/?p=4607"},"modified":"2025-01-29T13:35:58","modified_gmt":"2025-01-29T21:35:58","slug":"announcing-typescript-5-8-beta","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-5-8-beta\/","title":{"rendered":"Announcing TypeScript 5.8 Beta"},"content":{"rendered":"<p>Today we are excited to announce the availability of TypeScript 5.8 Beta.<\/p>\n<p>To get started using the beta, you can get it through npm with the following command:<\/p>\n<pre class=\"prettyprint language-sh\" style=\"padding: 10px;border-radius: 10px;\"><code>npm install -D typescript@beta\r\n<\/code><\/pre>\n<p>Let&#8217;s take a look at what&#8217;s new in TypeScript 5.8!<\/p>\n<h2 id=\"checked-returns-for-conditional-and-indexed-access-types\">Checked Returns for Conditional and Indexed Access Types<\/h2>\n<p>Consider an API that presents a set of options to a user:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>\/**\r\n * @param prompt The text to show to a user.\r\n * @param selectionKind Whether a user can select multiple options, or just a single option.\r\n * @param items Each of the options presented to the user.\r\n **\/\r\nasync function showQuickPick(\r\n    prompt: string,\r\n    selectionKind: SelectionKind,\r\n    items: readonly string[],\r\n): Promise&lt;string | string[]&gt; {\r\n    \/\/ ...\r\n}\r\n\r\nenum SelectionKind {\r\n    Single,\r\n    Multiple,\r\n}\r\n<\/code><\/pre>\n<p>The intent with <code>showQuickPick<\/code> is that it shows a UI element that can allow selecting either a single option or multiple options.\nWhen it does this is determined by the <code>selectionKind<\/code> parameter.\nWhen <code>selectionKind<\/code> is <code>SelectionKind.Single<\/code>, the return type of <code>showQuickPick<\/code> should be <code>string<\/code>, and when it is <code>SelectionKind.Multiple<\/code>, the return type should be <code>string[]<\/code>.<\/p>\n<p>The problem is that the type signature of <code>showQuickPick<\/code> doesn&#8217;t make this clear.\nIt just says that it eventually returns <code>string | string[]<\/code> &#8211; it could be a <code>string<\/code> and it could be a <code>string[]<\/code>, but callers have to explicitly check.\nIn our example below, we might expect <code>shoppingList<\/code> to have the type <code>string[]<\/code>, but we end up with the more broad <code>string | string[]<\/code>.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>let shoppingList = await showQuickPick(\r\n    &quot;Which fruits do you want to purchase?&quot;,\r\n    SelectionKind.Multiple,\r\n    [&quot;apples&quot;, &quot;oranges&quot;, &quot;bananas&quot;, &quot;durian&quot;],\r\n);\r\n\r\nconsole.log(`Alright, going out to buy some ${shoppingList.join(&quot;, &quot;)}`);\r\n\/\/                                                         ~~~~\r\n\/\/ error!\r\n\/\/ Property 'join' does not exist on type 'string | string[]'.\r\n\/\/  Property 'join' does not exist on type 'string'.\r\n<\/code><\/pre>\n<p>Instead, we can use a conditional type to make the return type of <code>showQuickPick<\/code> more precise:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>type QuickPickReturn&lt;S extends SelectionKind&gt; =\r\n    S extends SelectionKind.Multiple ? string[] : string\r\n\r\nasync function showQuickPick&lt;S extends SelectionKind&gt;(\r\n    prompt: string,\r\n    selectionKind: S,\r\n    items: readonly string[],\r\n): Promise&lt;QuickPickReturn&lt;S&gt;&gt; {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p>This works well for callers!<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ `SelectionKind.Multiple` gives a `string[]` - works \u2705\r\nlet shoppingList: string[] = await showQuickPick(\r\n    &quot;Which fruits do you want to purchase?&quot;,\r\n    SelectionKind.Multiple,\r\n    [&quot;apples&quot;, &quot;oranges&quot;, &quot;bananas&quot;, &quot;durian&quot;],\r\n);\r\n\r\n\/\/ `SelectionKind.Single` gives a `string` - works \u2705\r\nlet dinner: string = await showQuickPick(\r\n    &quot;What's for dinner tonight?&quot;,\r\n    SelectionKind.Single,\r\n    [&quot;sushi&quot;, &quot;pasta&quot;, &quot;tacos&quot;, &quot;ugh I'm too hungry to think, whatever you want&quot;],\r\n);\r\n<\/code><\/pre>\n<p>But what if we try to actually implement <code>showQuickPick<\/code>?<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>async function showQuickPick&lt;S extends SelectionKind&gt;(\r\n    prompt: string,\r\n    selectionKind: S,\r\n    items: readonly string[],\r\n): Promise&lt;QuickPickReturn&lt;S&gt;&gt; {\r\n    if (items.length &lt; 1) {\r\n        throw new Error(&quot;At least one item must be provided.&quot;);\r\n    }\r\n    \r\n    \/\/ Create buttons for every option.\r\n    let buttons = items.map(item =&gt; ({\r\n        selected: false,\r\n        text: item,\r\n    }));\r\n\r\n    \/\/ Default to the first element if necessary.\r\n    if (selectionKind === SelectionKind.Single) {\r\n        buttons[0].selected = true;\r\n    }\r\n\r\n    \/\/ Event handling code goes here...\r\n\r\n    \/\/ Figure out the selected items\r\n    const selectedItems = buttons\r\n        .filter(button =&gt; button.selected)\r\n        .map(button =&gt; button.text);\r\n\r\n    if (selectionKind === SelectionKind.Single) {\r\n        \/\/ Pick the first (only) selected item.\r\n        return selectedItems[0];\r\n    }\r\n    else {\r\n        \/\/ Return all selected items.\r\n        return selectedItems;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Unfortunately, TypeScript issues an error on each of the return statements.<\/p>\n<pre class=\"language-text\" style=\"padding: 10px;border-radius: 10px;\"><code>Type 'string[]' is not assignable to type 'QuickPickReturn&lt;S&gt;'.\r\nType 'string' is not assignable to type 'QuickPickReturn&lt;S&gt;'.\r\n<\/code><\/pre>\n<p>Until this point, TypeScript required a type assertion to implement any function returning a higher-order conditional type.<\/p>\n<pre class=\"prettyprint language-diff\" style=\"padding: 10px;border-radius: 10px;\"><code>      if (selectionKind === SelectionKind.Single) {\r\n          \/\/ Pick the first (only) selected item.\r\n-         return selectedItems[0];\r\n+         return selectedItems[0] as QuickPickReturn&lt;S&gt;;\r\n      }\r\n      else {\r\n          \/\/ Return all selected items.\r\n-         return selectedItems;\r\n+         return selectedItems as QuickPickReturn&lt;S&gt;;\r\n      }\r\n<\/code><\/pre>\n<p>This is not ideal because type assertions defeat legitimate checks that TypeScript would otherwise perform.\nFor example, it would be ideal if TypeScript could catch the following bug where we mixed up each branch of the <code>if<\/code>\/<code>else<\/code>:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>    if (selectionKind === SelectionKind.Single) {\r\n        \/\/ Oops! Returning an array when the caller expects a single item!\r\n        return selectedItems;\r\n    }\r\n    else {\r\n        \/\/ Oops! Returning a single item when the caller expects an array! \r\n        return selectedItems[0];\r\n    }\r\n<\/code><\/pre>\n<p>To avoid type assertions, TypeScript 5.8 now supports a limited form of checking against conditional types in return statements.\nWhen a function&#8217;s return type is a generic conditional type, TypeScript will now use control flow analysis for generic parameters whose types are used in the conditional type, instantiate the conditional type with the narrowed type of each parameter, and relate against that new type.<\/p>\n<p>What does this mean in practice?\nWell first, let&#8217;s look into what kinds of conditional types imply narrowing.\nTo mirror how narrowing operates in expressions, we have to be more explicit and exhaustive about what happens in each branch<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>type QuickPickReturn&lt;S extends SelectionKind&gt; =\r\n    S extends SelectionKind.Multiple ? string[] :\r\n    S extends SelectionKind.Single ? string :\r\n    never;\r\n<\/code><\/pre>\n<p>Once we&#8217;ve done this, everything in our example works.\nOur callers have no issue, and our implementation is now type-safe!\nAnd if we try swapping the contents of our <code>if<\/code> branches, TypeScript correctly flags it as an error!<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>    if (selectionKind === SelectionKind.Single) {\r\n        \/\/ Oops! Returning an array when the caller expects a single item!\r\n        return selectedItems;\r\n    \/\/  ~~~~~~\r\n    \/\/ error! Type 'string[]' is not assignable to type 'string'.\r\n    }\r\n    else {\r\n        \/\/ Oops! Returning a single item when the caller expects an array! \r\n        return selectedItems[0];\r\n    \/\/  ~~~~~~\r\n    \/\/ error! Type 'string[]' is not assignable to type 'string'.\r\n}\r\n<\/code><\/pre>\n<p>Note that TypeScript now also does something similar if we&#8217;re using indexed access types!<br>\nInstead of a conditional type, we can use a type that basically acts as a map from <code>SelectionKind<\/code> to the return type we want:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>interface QuickPickReturn {\r\n    [SelectionKind.Single]: string;\r\n    [SelectionKind.Multiple]: string[];\r\n}\r\n\r\nasync function showQuickPick&lt;S extends SelectionKind&gt;(\r\n    prompt: string,\r\n    selectionKind: S,\r\n    items: readonly string[],\r\n): Promise&lt;QuickPickReturn[S]&gt; {\r\n    \/\/ ...\r\n}\r\n<\/code><\/pre>\n<p>For many users, this will be a more ergonomic way to write the same code.<\/p>\n<p>For more information about this analysis, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/56941\">see the proposal and implementation here<\/a>!<\/p>\n<h3 id=\"a-note-on-limitations\">A Note on Limitations<\/h3>\n<p>There are some limitations to this feature.\nThis special checking only kicks in when a single parameter is associated with the type being checked against in a conditional type or used as a key in an indexed access type.\nIf using a conditional type, at least two checks must exist, with a terminal branch including <code>never<\/code>.\nThat parameter&#8217;s type must be generic and have a union type as its constraint.\nIn other words, the code analysis kicks in for a pattern like the following:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>function f&lt;T extends A | B&gt;(x: T):\r\n    T extends A ? string :\r\n    T extends B ? number :\r\n    never\r\n<\/code><\/pre>\n<p>This means these checks will not occur when a specific property is associated with a type parameter.\nFor example, if we rewrote our code above to use an options bag instead of individual parameters, TypeScript <em>would not<\/em> apply this new checking.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>interface QuickPickOptions&lt;S&gt; {\r\n    prompt: string,\r\n    selectionKind: S,\r\n    items: readonly string[]\r\n}\r\n\r\nasync function showQuickPick&lt;S extends SelectionKind&gt;(\r\n    options: QuickPickOptions&lt;S&gt;\r\n): Promise&lt;QuickPickReturn&lt;S&gt;&gt; {\r\n    \/\/ narrowing won't work correctly here...\r\n}\r\n<\/code><\/pre>\n<p>But workarounds may exist by writing a conditional type that checks against the inner contents.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>type QuickPickReturn&lt;O extends QuickPickOptionsBase&gt; =\r\n    O extends QuickPickOptionsMultiple ? string[] :\r\n    O extends QuickPickOptionsSingle ? string :\r\n    never;\r\n\r\ninterface QuickPickOptionsBase {\r\n    prompt: string,\r\n    items: readonly string[]\r\n}\r\ninterface QuickPickOptionsSingle extends QuickPickOptionsBase {\r\n    selectionKind: SelectionKind.Single;\r\n}\r\ninterface QuickPickOptionsMultiple extends QuickPickOptionsBase {\r\n    selectionKind: SelectionKind.Multiple;\r\n}\r\n\r\nasync function showQuickPick&lt;Opts extends QuickPickOptionsSingle | QuickPickOptionsMultiple&gt;(\r\n    options: Opts\r\n): Promise&lt;QuickPickReturn&lt;Opts&gt;&gt;\r\n<\/code><\/pre>\n<p>These rules may seem like quite a lot to remember, but in practice, most code will not need to leverage these higher-order types.\nAdditionally, while we prefer implementations use this new checking mechanism over type assertions, we do encourage users to keep their APIs simple when possible in the interest of keeping the types written simpler as well.\nIn the meantime, we will continue to explore ways to relax some of these limitations while making the code easy to author.<\/p>\n<h2 id=\"support-for-require-of-ecmascript-modules-in---module-nodenext\">Support for <code>require()<\/code> of ECMAScript Modules in <code>--module nodenext<\/code><\/h2>\n<p>For years, Node.js supported ECMAScript modules (ESM) alongside CommonJS modules.\nUnfortunately, the interoperability between the two had some challenges.<\/p>\n<ul>\n<li>ESM files could <code>import<\/code> CommonJS files<\/li>\n<li>CommonJS files <em><strong>could not<\/strong><\/em> <code>require()<\/code> ESM files<\/li>\n<\/ul>\n<p>In other words, consuming CommonJS files from ESM files was possible, but not the other way around.\nThis introduced many challenges for library authors who wanted to provide ESM support.\nThese library authors would either have to break compatibility with CommonJS users, &quot;dual-publish&quot; their libraries (providing separate entry-points for ESM and CommonJS), or just stay on CommonJS indefinitely.\nWhile dual-publishing might sound like a good middle-ground, it is a complex and error-prone process that also roughly doubles the amount of code within a package.<\/p>\n<p>Node.js 22 relaxes some of these restrictions and permits <code>require(&quot;esm&quot;)<\/code> calls from CommonJS modules to ECMAScript modules.\nNode.js still does not permit <code>require()<\/code> on ESM files that contain a top-level <code>await<\/code>, but most other ESM files are now consumable from CommonJS files.\nThis presents a major opportunity for library authors to provide ESM support without having to dual-publish their libraries.<\/p>\n<p>TypeScript 5.8 supports this behavior under the <code>--module nodenext<\/code> flag.\nWhen <code>--module nodenext<\/code> is enabled, TypeScript will avoid issuing errors on these <code>require()<\/code> calls to ESM files.<\/p>\n<p>Because this feature may be back-ported to older versions of Node.js, there is currently no stable <code>--module nodeXXXX<\/code> option that enables this behavior;\nhowever, we predict future versions of TypeScript may be able to stabilize the feature under <code>node20<\/code>.\nIn the meantime, we encourage users of Node.js 22 and newer to use <code>--module nodenext<\/code>, while library authors and users of older Node.js versions should remain on <code>--module node16<\/code> (or make the minor update to <a href=\"#--module-node18\"><code>--module node18<\/code><\/a>).<\/p>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/60761\">see our support for require(&quot;esm&quot;) here<\/a>.<\/p>\n<h2 id=\"module-node18\"><code>--module node18<\/code><\/h2>\n<p>TypeScript 5.8 introduces a stable <code>--module node18<\/code> flag.\nFor users who are fixed on using Node.js 18, this flag provides a stable point of reference that does not incorporate certain behaviors that are in <code>--module nodenext<\/code>.\nSpecifically:<\/p>\n<ul>\n<li><code>require()<\/code> of ECMAScript modules is disallowed under <code>node18<\/code>, but allowed under <code>nodenext<\/code><\/li>\n<li>import assertions (deprecated in favor of import attributes) are allowed under <code>node18<\/code>, but are disallowed under <code>nodenext<\/code><\/li>\n<\/ul>\n<p>See more at both <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/60722\">the <code>--module node18<\/code> pull request<\/a> and <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/60761\">changes made to <code>--module nodenext<\/code><\/a>.<\/p>\n<h2 id=\"the---erasablesyntaxonly-option\">The <code>--erasableSyntaxOnly<\/code> Option<\/h2>\n<p>Recently, Node.js 23.6 unflagged <a href=\"https:\/\/nodejs.org\/api\/typescript.html#type-stripping\">experimental support for running TypeScript files directly<\/a>;\nhowever, only certain constructs are supported under this mode.\nNode.js has unflagged a mode called <code>--experimental-strip-types<\/code> which requires that any TypeScript-specific syntax cannot have runtime semantics.\nPhrased differently, it must be possible to easily <em>erase<\/em> or &quot;strip out&quot; any TypeScript-specific syntax from a file, leaving behind a valid JavaScript file.<\/p>\n<p>That means constructs like the following are not supported:<\/p>\n<ul>\n<li><code>enum<\/code> declarations<\/li>\n<li><code>namespace<\/code>s and <code>module<\/code>s with runtime code<\/li>\n<li>parameter properties in classes<\/li>\n<li><code>import<\/code> aliases<\/li>\n<\/ul>\n<p>Similar tools like <a href=\"https:\/\/github.com\/bloomberg\/ts-blank-space\">ts-blank-space<\/a> or <a href=\"https:\/\/github.com\/nodejs\/amaro\">Amaro<\/a> (the underlying library for type-stripping in Node.js) have the same limitations.\nThese tools will provide helpful error messages if they encounter code that doesn&#8217;t meet these requirements, but you still won&#8217;t find out your code doesn&#8217;t work until you actually try to run it.<\/p>\n<p>That&#8217;s why TypeScript 5.8 introduces the <code>--erasableSyntaxOnly<\/code> flag.\nWhen this flag is enabled, TypeScript will only allow you to use constructs that can be erased from a file, and will issue an error if it encounters any constructs that cannot be erased.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>class C {\r\n    constructor(public x: number) { }\r\n    \/\/          ~~~~~~~~~~~~~~~~\r\n    \/\/ error! This syntax is not allowed when 'erasableSyntaxOnly' is enabled.\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/61011\">see the implementation here<\/a>.<\/p>\n<h2 id=\"the---libreplacement-flag\">The <code>--libReplacement<\/code> Flag<\/h2>\n<p>In TypeScript 4.5, we introduced the possibility of substituting the default <code>lib<\/code> files with custom ones.\nThis was based on the possibility of resolving a library file from packages named <code>@typescript\/lib-*<\/code>.\nFor example, you could lock your <code>dom<\/code> libraries onto a specific version of <a href=\"https:\/\/www.npmjs.com\/package\/@types\/web?activeTab=readme\">the <code>@types\/web<\/code> package<\/a> with the following <code>package.json<\/code>:<\/p>\n<pre class=\"prettyprint language-json\" style=\"padding: 10px;border-radius: 10px;\"><code>{\r\n    &quot;devDependencies&quot;: {\r\n       &quot;@typescript\/lib-dom&quot;: &quot;npm:@types\/web@0.0.199&quot;\r\n     }\r\n}\r\n<\/code><\/pre>\n<p>When installed, a package called <code>@typescript\/lib-dom<\/code> should exist, and TypeScript will currently always look it up when <code>dom<\/code> is implied by your settings.<\/p>\n<p>This is a powerful feature, but it also incurs a bit of extra work.\nEven if you&#8217;re not using this feature, TypeScript always performs this lookup, and has to watch for changes in <code>node_modules<\/code> in case a <code>lib<\/code>-replacement package <em>begins<\/em> to exist.<\/p>\n<p>TypeScript 5.8 introduces the <code>--libReplacement<\/code> flag, which allows you to disable this behavior.\nIf you&#8217;re not using <code>--libReplacement<\/code>, you can now disable it with <code>--libReplacement false<\/code>.\nIn the future <code>--libReplacement false<\/code> may become the default, so if you currently rely on the behavior you should consider explicitly enabling it with <code>--libReplacement true<\/code>.<\/p>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/61023\">see the change here<\/a>.<\/p>\n<h2 id=\"preserved-computed-property-names-in-declaration-files\">Preserved Computed Property Names in Declaration Files<\/h2>\n<p>In an effort to make computed properties have more predictable emit in declaration files, TypeScript 5.8 will consistently preserve entity names (<code>bareVariables<\/code> and <code>dotted.names.that.look.like.this<\/code>) in computed property names in classes.<\/p>\n<p>For example, consider the following code:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>export let propName = &quot;theAnswer&quot;;\r\n\r\nexport class MyClass {\r\n    [propName] = 42;\r\n\/\/  ~~~~~~~~~~\r\n\/\/ error!\r\n\/\/ A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.\r\n}\r\n<\/code><\/pre>\n<p>Previous versions of TypeScript would issue an error when generating a declaration file for this module, and a best-effort declaration file would generate an index signature.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>export declare let propName: string;\r\nexport declare class MyClass {\r\n    [x: string]: number;\r\n}\r\n<\/code><\/pre>\n<p>In TypeScript 5.8, the example code is now allowed, and the emitted declaration file will match what you wrote:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>export declare let propName: string;\r\nexport declare class MyClass {\r\n    [propName]: number;\r\n}\r\n<\/code><\/pre>\n<p>Note that this does not create statically-named properties on the class.\nYou&#8217;ll still end up with what is effectively an index signature like <code>[x: string]: number<\/code>, so for that use case, you&#8217;d need to use <code>unique symbol<\/code>s or literal types.<\/p>\n<p>Note that writing this code was and currently is an error under the <code>--isolatedDeclarations<\/code> flag;\nbut we expect that thanks to this change, computed property names will generally be permitted in declaration emit.<\/p>\n<p>Note that it&#8217;s possible (though unlikely) that a file compiled in TypeScript 5.8 may generate a declaration file that is not backward compatible in TypeScript 5.7 or earlier.<\/p>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/60052\">see the implementing PR<\/a>.<\/p>\n<h2 id=\"optimizations-on-program-loads-and-updates\">Optimizations on Program Loads and Updates<\/h2>\n<p>TypeScript 5.8 introduces a number of optimizations that can both improve the time to build up a program, and also to update a program based on a file change in either <code>--watch<\/code> mode or editor scenarios.<\/p>\n<p>First, TypeScript now <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/60812\">avoids array allocations that would be involved while normalizing paths<\/a>.\nTypically, path normalization would involve segmenting each portion of a path into an array of strings, normalizing the resulting path based on relative segments, and then joining them back together using a canonical separator.\nFor projects with many files, this can be a significant and repetitive amount of work.\nTypeScript now avoids allocating an array, and operates more directly on indexes of the original path.<\/p>\n<p>Additionally, when edits are made that don&#8217;t change the fundamental structure of a project, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/60754\">TypeScript now avoids re-validating the options provided to it<\/a> (e.g. the contents of a <code>tsconfig.json<\/code>).\nThis means, for example, that a simple edit might not require checking that the output paths of a project don&#8217;t conflict with the input paths.\nInstead, the results of the last check can be used.\nThis should make edits in large projects feel more responsive.<\/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=\"libdts\"><code>lib.d.ts<\/code><\/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\/issues\/60985\">see linked issues related to DOM and <code>lib.d.ts<\/code> updates for this version of TypeScript<\/a>.<\/p>\n<h3 id=\"restrictions-on-import-assertions-under---module-nodenext\">Restrictions on Import Assertions Under <code>--module nodenext<\/code><\/h3>\n<p>Import assertions were a proposed addition to ECMAScript to ensure certain properties of an import (e.g. &quot;this module is JSON, and is not intended to be executable JavaScript code&quot;).\nThey were reinvented as a proposal called <a href=\"https:\/\/github.com\/tc39\/proposal-import-attributes\">import attributes<\/a>.\nAs part of the transition, they swapped from using the <code>assert<\/code> keyword to using the <code>with<\/code> keyword.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>\/\/ An import assertion \u274c - not future-compatible with most runtimes.\r\nimport data from &quot;.\/data.json&quot; assert { type: &quot;json&quot; };\r\n\r\n\/\/ An import attribute \u2705 - the preferred way to import a JSON file.\r\nimport data from &quot;.\/data.json&quot; with { type: &quot;json&quot; };\r\n<\/code><\/pre>\n<p>Node.js 22 no longer accepts import assertions using the <code>assert<\/code> syntax.\nIn turn when <code>--module nodenext<\/code> is enabled in TypeScript 5.8, TypeScript will issue an error if it encounters an import assertion.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px;border-radius: 10px;\"><code>import data from &quot;.\/data.json&quot; assert { type: &quot;json&quot; };\r\n\/\/                             ~~~~~~\r\n\/\/ error! Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'\r\n<\/code><\/pre>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/60761\">see the change here<\/a><\/p>\n<h2 id=\"whats-next\">What&#8217;s Next?<\/h2>\n<p>At this point, TypeScript 5.8 is &quot;feature-stable&quot;.\nThe focus on TypeScript 5.8 will be bug fixes, polish, and certain low-risk editor features.\nWe&#8217;ll have a release candidate available in the next few weeks, followed by a stable release soon after.\nIf you&#8217;re interested in planning around the release, be sure to <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/61023\">keep an eye on our iteration plan<\/a> which has target release dates and more.<\/p>\n<p>As a note: while beta is a great way to try out the next version of TypeScript, you can also <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/nightly-builds.html\">try a nightly build<\/a> to get the most up-to-date version of TypeScript 5.8 up until our release candidate.\nOur nightlies are well-tested and can even be used <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-vscode.vscode-typescript-next\">solely in your editor<\/a>.<\/p>\n<p>So please try out the beta or a nightly release today and let us know what you think!<\/p>\n<p>Happy Hacking!<\/p>\n<p>&#8211; Daniel Rosenwasser and the TypeScript Team<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today we are excited to announce the availability of TypeScript 5.8 Beta. To get started using the beta, you can get it through npm with the following command: npm install -D typescript@beta Let&#8217;s take a look at what&#8217;s new in TypeScript 5.8! Checked Returns for Conditional and Indexed Access Types Consider an API that presents [&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-4607","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-typescript"],"acf":[],"blog_post_summary":"<p>Today we are excited to announce the availability of TypeScript 5.8 Beta. To get started using the beta, you can get it through npm with the following command: npm install -D typescript@beta Let&#8217;s take a look at what&#8217;s new in TypeScript 5.8! Checked Returns for Conditional and Indexed Access Types Consider an API that presents [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/posts\/4607","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=4607"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/posts\/4607\/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=4607"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/categories?post=4607"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/tags?post=4607"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}