{"id":3078,"date":"2021-08-26T13:25:58","date_gmt":"2021-08-26T21:25:58","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/typescript\/?p=3078"},"modified":"2026-02-08T17:14:32","modified_gmt":"2026-02-09T01:14:32","slug":"announcing-typescript-4-4","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-4-4\/","title":{"rendered":"Announcing TypeScript 4.4"},"content":{"rendered":"<p>Today we&#8217;re excited to announce the availability of TypeScript 4.4!<\/p>\n<p>If you haven&#8217;t heard of TypeScript yet, it&#8217;s a language that builds on JavaScript by adding syntax for <em>static types<\/em>.\nTools like the TypeScript compiler just erase those types, leaving you with clean readable JavaScript that you can run anywhere;\nbut those types are there to be <em>type-checked<\/em>!\nTypes make your intentions explicit, and tools can read and understand them to catch errors before you even run your code.\nBy checking your types, TypeScript can catch errors like typos, logic errors, and more!\nBeyond catching errors, TypeScript also uses types to power editor tooling like accurate code-completion, go-to-definition, and renaming.\nYou can learn more <a href=\"https:\/\/www.typescriptlang.org\/\">on the TypeScript website<\/a>.<\/p>\n<p>To get started using TypeScript 4.4, you can get it <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.TypeScript.MSBuild\">through NuGet<\/a>, or use npm with the following command:<\/p>\n<pre class=\"prettyprint language-sh\" style=\"padding: 10px; border-radius: 10px;\"><code>npm install typescript\r\n<\/code><\/pre>\n<p>You can also get editor support by<\/p>\n<ul>\n<li><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=TypeScriptTeam.TypeScript-442\">Downloading for Visual Studio 2019\/2017<\/a><\/li>\n<li><a href=\"http:\/\/code.visualstudio.com\/insiders\">Installing the Insiders Version of Visual Studio Code<\/a> or following directions to <a href=\"https:\/\/code.visualstudio.com\/docs\/typescript\/typescript-compiling#_using-newer-typescript-versions\">use a newer version of TypeScript<\/a><\/li>\n<li><a href=\"https:\/\/packagecontrol.io\/packages\/TypeScript\">Using Package Control for Sublime Text 3<\/a>.<\/li>\n<\/ul>\n<p>Some major highlights of TypeScript 4.4 are:<\/p>\n<ul>\n<li><a href=\"#cfa-aliased-conditions\">Control Flow Analysis of Aliased Conditions and Discriminants<\/a><\/li>\n<li><a href=\"#symbol-template-signatures\">Symbol and Template String Pattern Index Signatures<\/a><\/li>\n<li><a href=\"#use-unknown-catch-variables\">Defaulting to the <code>unknown<\/code> Type in Catch Variables (<code>--useUnknownInCatchVariables<\/code>)<\/a><\/li>\n<li><a href=\"#exact-optional-property-types\">Exact Optional Property Types (<code>--exactOptionalPropertyTypes<\/code>)<\/a><\/li>\n<li><a href=\"#static-blocks\">Class <code>static<\/code> Blocks<\/a><\/li>\n<li><a href=\"#tsc-help\"><code>tsc --help<\/code> Updates and Improvements<\/a><\/li>\n<li><a href=\"#perf-improvements\">Performance Improvements<\/a><\/li>\n<li><a href=\"#spelling-corrections-js\">Spelling Suggestions for JavaScript<\/a><\/li>\n<li><a href=\"#inlay-hints\">Inlay Hints<\/a><\/li>\n<li><a href=\"#breaking-changes\">Breaking Changes<\/a><\/li>\n<\/ul>\n<p>Let&#8217;s explore these in more detail!<\/p>\n<h2 id=\"control-flow-analysis-of-aliased-conditions-and-discriminants\"><a name=\"cfa-aliased-conditions\"><\/a> Control Flow Analysis of Aliased Conditions and Discriminants<\/h2>\n<p>In JavaScript, we often have to probe a value in different ways, and do something different once we know more about its type.\nTypeScript understands these checks and calls them <em>type guards<\/em>.\nInstead of having to convince TypeScript of a variable&#8217;s type whenever we use it, the type-checker leverages something called <em>control flow analysis<\/em> to see if we&#8217;ve used a type guard before a given piece of code.<\/p>\n<p>For example, we can write something like<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>function foo(arg: unknown) {\r\n    if (typeof arg === \"string\") {\r\n        \/\/ We know 'arg' is a string now.\r\n        console.log(arg.toUpperCase());\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>In this example, we checked whether <code>arg<\/code> was a <code>string<\/code>.\nTypeScript recognized the <code>typeof arg === \"string\"<\/code> check, which it considered a type guard, and knew that <code>arg<\/code> was a <code>string<\/code> inside the body of the <code>if<\/code> block.\nThat let us access <code>string<\/code> methods like <code>toUpperCase()<\/code> without getting an error.<\/p>\n<p>However, what would happen if we moved the condition out to a constant called <code>argIsString<\/code>?<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>function foo(arg: unknown) {\r\n    const argIsString = typeof arg === \"string\";\r\n    if (argIsString) {\r\n        console.log(arg.toUpperCase());\r\n        \/\/              ~~~~~~~~~~~\r\n        \/\/ Error! Property 'toUpperCase' does not exist on type 'unknown'.\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>In previous versions of TypeScript, this would be an error &#8211; even though <code>argIsString<\/code> was assigned the value of a type guard, TypeScript simply lost that information.\nThat&#8217;s unfortunate since we might want to re-use the same check in several places.\nTo get around that, users often have to repeat themselves or use type assertions (a.k.a. casts).<\/p>\n<p>In TypeScript 4.4, that is no longer the case.\nThe above example works with no errors!\nWhen TypeScript sees that we are testing a constant value, it will do a little bit of extra work to see if it contains a type guard.\nIf that type guard operates on a <code>const<\/code>, a <code>readonly<\/code> property, or an un-modified parameter, then TypeScript is able to narrow that value appropriately.<\/p>\n<p>Different sorts of type guard conditions are preserved &#8211; not just <code>typeof<\/code> checks.\nFor example, checks on discriminated unions work like a charm.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>type Shape =\r\n    | { kind: \"circle\", radius: number }\r\n    | { kind: \"square\", sideLength: number };\r\n\r\nfunction area(shape: Shape): number {\r\n    const isCircle = shape.kind === \"circle\";\r\n    if (isCircle) {\r\n        \/\/ We know we have a circle here!\r\n        return Math.PI * shape.radius ** 2;\r\n    }\r\n    else {\r\n        \/\/ We know we're left with a square here!\r\n        return shape.sideLength ** 2;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Analysis on discriminants in 4.4 also goes a little bit deeper &#8211; we can now extract out discriminants and TypeScript can narrow the original object.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>type Shape =\r\n    | { kind: \"circle\", radius: number }\r\n    | { kind: \"square\", sideLength: number };\r\n\r\nfunction area(shape: Shape): number {\r\n    \/\/ Extract out the 'kind' field first.\r\n    const { kind } = shape;\r\n\r\n    if (kind === \"circle\") {\r\n        \/\/ We know we have a circle here!\r\n        return Math.PI * shape.radius ** 2;\r\n    }\r\n    else {\r\n        \/\/ We know we're left with a square here!\r\n        return shape.sideLength ** 2;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>As another example, here&#8217;s a function that checks whether two of its inputs have contents.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>function doSomeChecks(\r\n    inputA: string | undefined,\r\n    inputB: string | undefined,\r\n    shouldDoExtraWork: boolean,\r\n) {\r\n    let mustDoWork = inputA &amp;&amp; inputB &amp;&amp; shouldDoExtraWork;\r\n    if (mustDoWork) {\r\n        \/\/ We can access 'string' properties on both 'inputA' and 'inputB'!\r\n        const upperA = inputA.toUpperCase();\r\n        const upperB = inputB.toUpperCase();\r\n        \/\/ ...\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>TypeScript can understand that both <code>inputA<\/code> and <code>inputB<\/code> are both present if <code>mustDoWork<\/code> is <code>true<\/code>.\nThat means we don&#8217;t have to write a non-null assertion like <code>inputA!<\/code> to convince TypeScript that <code>inputA<\/code> isn&#8217;t <code>undefined<\/code>.<\/p>\n<p>One neat feature here is that this analysis works transitively.\nTypeScript will hop through constants to understand what sorts of checks you&#8217;ve already performed.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>function f(x: string | number | boolean) {\r\n    const isString = typeof x === \"string\";\r\n    const isNumber = typeof x === \"number\";\r\n    const isStringOrNumber = isString || isNumber;\r\n    if (isStringOrNumber) {\r\n        x;  \/\/ Type of 'x' is 'string | number'.\r\n    }\r\n    else {\r\n        x;  \/\/ Type of 'x' is 'boolean'.\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Note that there&#8217;s a cutoff &#8211; TypeScript doesn&#8217;t go arbitrarily deep when checking these conditions, but its analysis is deep enough for most checks.<\/p>\n<p>This feature should make a lot of intuitive JavaScript code &#8220;just work&#8221; in TypeScript without it getting in your way.\nFor more details, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44730\">check out the implementation on GitHub<\/a>!<\/p>\n<h2 id=\"symbol-and-template-string-pattern-index-signatures\"><a name=\"symbol-template-signatures\"><\/a> Symbol and Template String Pattern Index Signatures<\/h2>\n<p>TypeScript lets us describe objects where every property has to have a certain type using <em>index signatures<\/em>.\nThis allows us to use these objects as dictionary-like types, where we can use string keys to index into them with square brackets.<\/p>\n<p>For example, we can write a type with an index signature that takes <code>string<\/code> keys and maps to <code>boolean<\/code> values.\nIf we try to assign anything other than a <code>boolean<\/code> value, we&#8217;ll get an error.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>interface BooleanDictionary {\r\n    [key: string]: boolean;\r\n}\r\n\r\ndeclare let myDict: BooleanDictionary;\r\n\r\n\/\/ Valid to assign boolean values\r\nmyDict[\"foo\"] = true;\r\nmyDict[\"bar\"] = false;\r\n\r\n\/\/ Error, \"oops\" isn't a boolean\r\nmyDict[\"baz\"] = \"oops\";\r\n<\/code><\/pre>\n<p>While <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\">a <code>Map<\/code> might be a better data structure here<\/a> (specifically, a <code>Map&lt;string, boolean&gt;<\/code>), JavaScript objects are often more convenient to use or just happen to be what we&#8217;re given to work with.<\/p>\n<p>Similarly, <code>Array&lt;T&gt;<\/code> already defines a <code>number<\/code> index signature that lets us insert\/retrieve values of type <code>T<\/code>.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>\/\/ This is part of TypeScript's definition of the built-in Array type.\r\ninterface Array&lt;T&gt; {\r\n    [index: number]: T;\r\n\r\n    \/\/ ...\r\n}\r\n\r\nlet arr = new Array&lt;string&gt;();\r\n\r\n\/\/ Valid\r\narr[0] = \"hello!\";\r\n\r\n\/\/ Error, expecting a 'string' value here\r\narr[1] = 123;\r\n<\/code><\/pre>\n<p>Index signatures are very useful to express lots of code out in the wild;\nhowever, until now they&#8217;ve been limited to <code>string<\/code> and <code>number<\/code> keys (and <code>string<\/code> index signatures have an intentional quirk where they can accept <code>number<\/code> keys since they&#8217;ll be coerced to strings anyway).\nThat means that TypeScript didn&#8217;t allow indexing objects with <code>symbol<\/code> keys.\nTypeScript also couldn&#8217;t model an index signature of some <em>subset<\/em> of <code>string<\/code> keys &#8211; for example, an index signature which describes just properties whose names start with the text <code>data-<\/code>.<\/p>\n<p>TypeScript 4.4 addresses these limitations, and allows index signatures for <code>symbol<\/code>s and template string patterns.<\/p>\n<p>For example, TypeScript now allows us to declare a type that can be keyed on arbitrary <code>symbol<\/code>s.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>interface Colors {\r\n    [sym: symbol]: number;\r\n}\r\n\r\nconst red = Symbol(\"red\");\r\nconst green = Symbol(\"green\");\r\nconst blue = Symbol(\"blue\");\r\n\r\nlet colors: Colors = {};\r\n\r\ncolors[red] = 255;          \/\/ Assignment of a number is allowed\r\nlet redVal = colors[red];   \/\/ 'redVal' has the type 'number'\r\n\r\ncolors[blue] = \"da ba dee\"; \/\/ Error: Type 'string' is not assignable to type 'number'.\r\n<\/code><\/pre>\n<p>Similarly, we can write an index signature with template string pattern type.\nOne use of this might be to exempt properties starting with <code>data-<\/code> from TypeScript&#8217;s excess property checking.\nWhen we pass an object literal to something with an expected type, TypeScript will look for excess properties that weren&#8217;t declared in the expected type.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>interface Options {\r\n    width?: number;\r\n    height?: number;\r\n}\r\n\r\nlet a: Options = {\r\n    width: 100,\r\n    height: 100,\r\n    \"data-blah\": true, \/\/ Error! 'data-blah' wasn't declared in 'Options'.\r\n};\r\n\r\ninterface OptionsWithDataProps extends Options {\r\n    \/\/ Permit any property starting with 'data-'.\r\n    [optName: `data-${string}`]: unknown;\r\n}\r\n\r\nlet b: OptionsWithDataProps = {\r\n    width: 100,\r\n    height: 100,\r\n    \"data-blah\": true,       \/\/ Works!\r\n\r\n    \"unknown-property\": true,  \/\/ Error! 'unknown-property' wasn't declared in 'OptionsWithDataProps'.\r\n};\r\n<\/code><\/pre>\n<p>A final note on index signatures is that they now permit union types, as long as they&#8217;re a union of infinite-domain primitive types &#8211; specifically:<\/p>\n<ul>\n<li><code>string<\/code><\/li>\n<li><code>number<\/code><\/li>\n<li><code>symbol<\/code><\/li>\n<li>template string patterns (e.g. <code>`hello-${string}`<\/code>)<\/li>\n<\/ul>\n<p>An index signature whose argument is a union of these types will de-sugar into several different index signatures.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>interface Data {\r\n    [optName: string | symbol]: any;\r\n}\r\n\r\n\/\/ Equivalent to\r\n\r\ninterface Data {\r\n    [optName: string]: any;\r\n    [optName: symbol]: any;\r\n}\r\n<\/code><\/pre>\n<p>For more details, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44512\">read up on the pull request<\/a><\/p>\n<h2 id=\"defaulting-to-the-unknown-type-in-catch-variables---useunknownincatchvariables\"><a name=\"use-unknown-catch-variables\"><\/a> Defaulting to the <code>unknown<\/code> Type in Catch Variables (<code>--useUnknownInCatchVariables<\/code>)<\/h2>\n<p>In JavaScript, any type of value can be thrown with <code>throw<\/code> and caught in a <code>catch<\/code> clause.\nBecause of this, TypeScript historically typed catch clause variables as <code>any<\/code>, and would not allow any other type annotation:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>try {\r\n    \/\/ Who knows what this might throw...\r\n    executeSomeThirdPartyCode();\r\n}\r\ncatch (err) { \/\/ err: any\r\n    console.error(err.message); \/\/ Allowed, because 'any'\r\n    err.thisWillProbablyFail(); \/\/ Allowed, because 'any' :(\r\n}\r\n<\/code><\/pre>\n<p>Once TypeScript added the <code>unknown<\/code> type, it became clear that <code>unknown<\/code> was a better choice than <code>any<\/code> in <code>catch<\/code> clause variables for users who want the highest degree of correctness and type-safety, since it narrows better and forces us to test against arbitrary values.\nEventually TypeScript 4.0 allowed users to specify an explicit type annotation of <code>unknown<\/code> (or <code>any<\/code>) on each <code>catch<\/code> clause variable so that we could opt into stricter types on a case-by-case basis;\nhowever, for some, manually specifying <code>: unknown<\/code> on every <code>catch<\/code> clause was a chore.<\/p>\n<p>That&#8217;s why TypeScript 4.4 introduces a new flag called <code>--useUnknownInCatchVariables<\/code>.\nThis flag changes the default type of <code>catch<\/code> clause variables from <code>any<\/code> to <code>unknown<\/code>.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>try {\r\n    executeSomeThirdPartyCode();\r\n}\r\ncatch (err) { \/\/ err: unknown\r\n\r\n    \/\/ Error! Property 'message' does not exist on type 'unknown'.\r\n    console.error(err.message);\r\n\r\n    \/\/ Works! We can narrow 'err' from 'unknown' to 'Error'.\r\n    if (err instanceof Error) {\r\n        console.error(err.message);\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>This flag is enabled under the <code>--strict<\/code> family of options.\nThat means that if you check your code using <code>--strict<\/code>, this option will automatically be turned on.\nYou may end up with errors in TypeScript 4.4 such as<\/p>\n<pre class=\"language-text\" style=\"padding: 10px; border-radius: 10px;\"><code>Property 'message' does not exist on type 'unknown'.\r\nProperty 'name' does not exist on type 'unknown'.\r\nProperty 'stack' does not exist on type 'unknown'.\r\n<\/code><\/pre>\n<p>In cases where we don&#8217;t want to deal with an <code>unknown<\/code> variable in a <code>catch<\/code> clause, we can always add an explicit <code>: any<\/code> annotation so that we can opt <em>out<\/em> of stricter types.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>try {\r\n    executeSomeThirdPartyCode();\r\n}\r\ncatch (err: any) {\r\n    console.error(err.message); \/\/ Works again!\r\n}\r\n<\/code><\/pre>\n<p>For more information, take a look at <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/41013\">the implementing pull request<\/a>.<\/p>\n<h2 id=\"exact-optional-property-types---exactoptionalpropertytypes\"><a name=\"exact-optional-property-types\"><\/a> Exact Optional Property Types (<code>--exactOptionalPropertyTypes<\/code>)<\/h2>\n<p>In JavaScript, reading a <em>missing<\/em> property on an object produces the value <code>undefined<\/code>.\nIt&#8217;s also possible to <em>have<\/em> an actual property with the value <code>undefined<\/code>.\nA lot of code in JavaScript tends to treat these situations the same way, and so initially TypeScript just interpreted every optional property as if a user had written <code>undefined<\/code> in the type.\nFor example,<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>interface Person {\r\n    name: string,\r\n    age?: number;\r\n}\r\n<\/code><\/pre>\n<p>was considered equivalent to<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>interface Person {\r\n    name: string,\r\n    age?: number | undefined;\r\n}\r\n<\/code><\/pre>\n<p>What this meant is that a user could explicitly write <code>undefined<\/code> in place of <code>age<\/code>.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>const p: Person = {\r\n    name: \"Daniel\",\r\n    age: undefined, \/\/ This is okay by default.\r\n};\r\n<\/code><\/pre>\n<p>So by default, TypeScript doesn&#8217;t distinguish between a present property with the value <code>undefined<\/code> and a missing property.\nWhile this works most of the time, not all code in JavaScript makes the same assumptions.\nFunctions and operators like <code>Object.assign<\/code>, <code>Object.keys<\/code>, object spread (<code>{ ...obj }<\/code>), and <code>for<\/code>&#8211;<code>in<\/code> loops behave differently depending on whether or not a property actually exists on an object.\nIn the case of our <code>Person<\/code> example, this could potentially lead to runtime errors if the <code>age<\/code> property was observed in a context where its presence was important.<\/p>\n<p>In TypeScript 4.4, the new flag <code>--exactOptionalPropertyTypes<\/code> specifies that optional property types should be interpreted exactly as written, meaning that <code> | undefined<\/code> is not added to the type:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>\/\/ With 'exactOptionalPropertyTypes' on:\r\nconst p: Person = {\r\n    name: \"Daniel\",\r\n    age: undefined, \/\/ Error! undefined isn't a number\r\n};\r\n<\/code><\/pre>\n<p>This flag is not part of the <code>--strict<\/code> family and needs to be turned on explicitly if you&#8217;d like this behavior.\nIt also requires <code>--strictNullChecks<\/code> to be enabled as well.\nWe&#8217;ll be making updates to DefinitelyTyped and other definitions to try to make the transition as straightforward as possible, but you may encounter some friction with this depending on how your code is structured.<\/p>\n<p>For more information, you can <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/43947\">take a look at the implementing pull request here<\/a>.<\/p>\n<h2 id=\"static-blocks-in-classes\"><a name=\"static-blocks\"><\/a> <code>static<\/code> Blocks in Classes<\/h2>\n<p>TypeScript 4.4 brings support for <a href=\"https:\/\/github.com\/tc39\/proposal-class-static-block#ecmascript-class-static-initialization-blocks\"><code>static<\/code> blocks in classes<\/a>, an upcoming ECMAScript feature that can help you write more-complex initialization code for static members.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>class Foo {\r\n    static count = 0;\r\n\r\n    \/\/ This is a static block:\r\n    static {\r\n        if (someCondition()) {\r\n            Foo.count++;\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>These static blocks allow you to write a sequence of statements with their own scope that can access private fields within the containing class.\nThat means that we can write initialization code with all the capabilities of writing statements, no leakage of variables, and full access to our class&#8217;s internals.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>class Foo {\r\n    static #count = 0;\r\n\r\n    get count() {\r\n        return Foo.#count;\r\n    }\r\n\r\n    static {\r\n        try {\r\n            const lastInstances = loadLastInstances();\r\n            Foo.#count += lastInstances.length;\r\n        }\r\n        catch {}\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Without <code>static<\/code> blocks, writing the code above was possible, but often involved several different types of hacks that had to compromise in some way.<\/p>\n<p>Note that a class can have multiple <code>static<\/code> blocks, and they&#8217;re run in the same order in which they&#8217;re written.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>\/\/ Prints:\r\n\/\/    1\r\n\/\/    2\r\n\/\/    3\r\nclass Foo {\r\n    static prop = 1\r\n    static {\r\n        console.log(Foo.prop++);\r\n    }\r\n    static {\r\n        console.log(Foo.prop++);\r\n    }\r\n    static {\r\n        console.log(Foo.prop++);\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>We&#8217;d like to extend our thanks to <a href=\"https:\/\/github.com\/Kingwl\">Wenlu Wang<\/a> for TypeScript&#8217;s implementation of this feature.\nFor more details, you can <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/43370\">see that pull request here<\/a>.<\/p>\n<h2 id=\"tsc---help-updates-and-improvements\"><a name=\"tsc-help\"><\/a> <code>tsc --help<\/code> Updates and Improvements<\/h2>\n<p>TypeScript&#8217;s <code>--help<\/code> option has gotten a refresh!\nThanks to work in part by <a href=\"https:\/\/github.com\/ShuiRuTian\">Song Gao<\/a>, we&#8217;ve brought in changes to <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44409\">update the descriptions of our compiler options<\/a> and <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44157\">restyle the <code>--help<\/code> menu<\/a> with colors and other visual separation.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/typescript\/wp-content\/uploads\/sites\/11\/2021\/08\/tsc-help-ps-wt-4-4.png\" alt=\"The new TypeScript menu where the output is bucketed into several different areas\" \/><\/p>\n<p>You can read more on <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/44074\">the original proposal thread<\/a>.<\/p>\n<h2 id=\"performance-improvements\"><a name=\"perf-improvements\"><\/a> Performance Improvements<\/h2>\n<h3 id=\"faster-declaration-emit\">Faster Declaration Emit<\/h3>\n<p>TypeScript now caches whether internal symbols are accessible in different contexts, along with how specific types should be printed.\nThese changes can improve TypeScript&#8217;s general performance in code with fairly complex types, and is especially observed when emitting <code>.d.ts<\/code> files under the <code>--declaration<\/code> flag.<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/43973\">See more details here<\/a>.<\/p>\n<h3 id=\"faster-path-normalization\">Faster Path Normalization<\/h3>\n<p>TypeScript often has to do several types of &#8220;normalization&#8221; on file paths to get them into a consistent format that the compiler can use everywhere.\nThis involves things like replacing backslashes with slashes, or removing intermediate <code>\/.\/<\/code> and <code>\/..\/<\/code> segments of paths.\nWhen TypeScript has to operates over millions of these paths, these operations end up being a bit slow.\nIn TypeScript 4.4, paths first undergo quick checks to see whether they need any normalization in the first place.\nThese improvements together reduce project load time by 5-10% on bigger projects, and significantly more in massive projects that we&#8217;ve tested internally.<\/p>\n<p>For more details, you can <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44173\">view the PR for path segment normalization<\/a> along with <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44100\">the PR for slash normalization<\/a>.<\/p>\n<h3 id=\"faster-path-mapping\">Faster Path Mapping<\/h3>\n<p>TypeScript now caches the way it constructs path-mappings (using the <code>paths<\/code> option in <code>tsconfig.json<\/code>).\nFor projects with several hundred mappings, the reduction is significant.\nYou can see more <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44078\">on the change itself<\/a>.<\/p>\n<h3 id=\"faster-incremental-builds-with---strict\">Faster Incremental Builds with <code>--strict<\/code><\/h3>\n<p>In what was effectively a bug, TypeScript would end up redoing type-checking work under <code>--incremental<\/code> compilations if <code>--strict<\/code> was on.\nThis led to many builds being just as slow as if <code>--incremental<\/code> was turned off.\nTypeScript 4.4 fixes this, though the change has also been back-ported to TypeScript 4.3.<\/p>\n<p>See more <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44394\">here<\/a>.<\/p>\n<h3 id=\"faster-source-map-generation-for-big-outputs\">Faster Source Map Generation for Big Outputs<\/h3>\n<p>TypeScript 4.4 adds an optimization for source map generation on extremely large output files.\nWhen building an older version of the TypeScript compiler, this results in around an 8% reduction in emit time.<\/p>\n<p>We&#8217;d like to extend our thanks to <a href=\"https:\/\/github.com\/dmichon-msft\">David Michon<\/a> who provided a <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44031\">simple and clean change<\/a> to enable this performance win.<\/p>\n<h3 id=\"faster---force-builds\">Faster <code>--force<\/code> Builds<\/h3>\n<p>When using <code>--build<\/code> mode on project references, TypeScript has to perform up-to-date checks to determine which files need to be rebuilt.\nWhen performing a <code>--force<\/code> build, however, that information is irrelevant since every project dependency will be rebuilt from scratch.\nIn TypeScript 4.4, <code>--force<\/code> builds avoid those unnecessary steps and start a full build.\nSee more about the change <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/43666\">here<\/a>.<\/p>\n<h2 id=\"spelling-suggestions-for-javascript\"><a name=\"spelling-corrections-js\"><\/a> Spelling Suggestions for JavaScript<\/h2>\n<p>TypeScript powers the JavaScript editing experience in editors like Visual Studio and Visual Studio Code.\nMost of the time, TypeScript tries to stay out of the way in JavaScript files;\nhowever, TypeScript often has a lot of information to make confident suggestions, and ways of surfacing suggestions that aren&#8217;t <em>too<\/em> invasive.<\/p>\n<p>That&#8217;s why TypeScript now issues spelling suggestions in plain JavaScript files &#8211; ones without <code>\/\/ @ts-check<\/code> or in a project with <code>checkJs<\/code> turned off.\nThese are the same <em>&#8220;Did you mean&#8230;?&#8221;<\/em> suggestions that TypeScript files already have, and now they&#8217;re available in <em>all<\/em> JavaScript files in some form.<\/p>\n<p>These spelling suggestions can provide a subtle clue that your code is wrong.\nWe managed to find a few bugs in existing code while testing this feature!<\/p>\n<p>For more details on this new feature, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44271\">take a look at the pull request<\/a>!<\/p>\n<h2 id=\"inlay-hints\"><a name=\"inlay-hints\"><\/a> Inlay Hints<\/h2>\n<p>TypeScript 4.4 provides support for <em>inlay hints<\/em> which can help display useful information like parameter names and return types in your code.\nYou can think of it as a sort of friendly &#8220;ghost text&#8221;.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/typescript\/wp-content\/uploads\/sites\/11\/2021\/08\/inlayHints-4.4-rc-ghd.png\" alt=\"A preview of inlay hints in Visual Studio Code\" \/><\/p>\n<p>This feature was built by <a href=\"https:\/\/github.com\/Kingwl\">Wenlu Wang<\/a> whose <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/42089\">pull request<\/a> has more details.<\/p>\n<p>Wenlu also contributed <a href=\"https:\/\/github.com\/microsoft\/vscode\/pull\/113412\">the integration for inlay hints in Visual Studio Code<\/a> which has shipped as <a href=\"https:\/\/code.visualstudio.com\/updates\/v1_59#_typescript-44\">part of the July 2021 (1.59) release<\/a>.\nIf you&#8217;d like to try inlay hints out, make sure you&#8217;re using a recent <a href=\"https:\/\/code.visualstudio.com\/updates\/v1_59\">stable<\/a> or <a href=\"https:\/\/code.visualstudio.com\/insiders\/\">insiders<\/a> version of the editor.\nYou can also modify when and where inlay hints get displayed in Visual Studio Code&#8217;s settings.<\/p>\n<h2 id=\"auto-imports-show-true-paths-in-completion-lists\"><a name=\"auto-import-specifiers\"><\/a> Auto-Imports Show True Paths in Completion Lists<\/h2>\n<p>When editors like Visual Studio Code show a completion list, completions which include auto-imports are displayed with a path to the given module;\nhowever, this path usually isn&#8217;t what TypeScript ends up placing in a module specifier.\nThe path is usually something relative to the <em>workspace<\/em>, meaning that if you&#8217;re importing from a package like <code>moment<\/code>, you&#8217;ll often see a path like <code>node_modules\/moment<\/code>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/typescript\/wp-content\/uploads\/sites\/11\/2021\/08\/completion-import-labels-pre-4-4.png\" alt=\"A completion list containing unwieldy paths containing 'node_modules'. For example, the label for 'calendarFormat' is 'node_modules\/moment\/moment' instead of 'moment'.\" \/><\/p>\n<p>These paths end up being unwieldy and often misleading, especially given that the path that actually gets inserted into your file needs to consider Node&#8217;s <code>node_modules<\/code> resolution, path mappings, symlinks, and re-exports.<\/p>\n<p>That&#8217;s why with TypeScript 4.4, the completion item label now shows the <em>actual<\/em> module path that will be used for the import!<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/typescript\/wp-content\/uploads\/sites\/11\/2021\/08\/completion-import-labels-4-4.png\" alt=\"A completion list containing clean paths with no intermediate 'node_modules'. For example, the label for 'calendarFormat' is 'moment' instead of 'node_modules\/moment\/moment'.\" \/><\/p>\n<p>Since this calculation can be expensive, completion lists containing many auto-imports may fill in the final module specifiers in batches as you type more characters. It&#8217;s possible that you&#8217;ll still sometimes see the old workspace-relative path labels; however, as your editing experience &#8220;warms up&#8221;, they should get replaced with the actual path after another keystroke or two.<\/p>\n<p>For more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44713\">see the original change here<\/a>.<\/p>\n<h2 id=\"breaking-changes\"><a name=\"breaking-changes\"><\/a> Breaking Changes<\/h2>\n<h3 id=\"libdts-changes-for-typescript-44\"><code>lib.d.ts<\/code> Changes for TypeScript 4.4<\/h3>\n<p>As with every TypeScript version, declarations for <code>lib.d.ts<\/code> (especially the declarations generated for web contexts), have changed.\nYou can consult <a href=\"https:\/\/github.com\/microsoft\/TypeScript-DOM-lib-generator\/issues\/1029#issuecomment-869224737\">our list of known <code>lib.dom.d.ts<\/code> changes<\/a> to understand what is impacted.<\/p>\n<h3 id=\"more-compliant-indirect-calls-for-imported-functions\">More-Compliant Indirect Calls for Imported Functions<\/h3>\n<p>In earlier versions of TypeScript, calling an import from CommonJS, AMD, and other non-ES module systems would set the <code>this<\/code> value of the called function.\nSpecifically, in the following example, when calling <code>fooModule.foo()<\/code>, the <code>foo()<\/code> method will have <code>fooModule<\/code> set as the value of <code>this<\/code>.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>\/\/ Imagine this is our imported module, and it has an export named 'foo'.\r\nlet fooModule = {\r\n    foo() {\r\n        console.log(this);\r\n    }\r\n};\r\n\r\nfooModule.foo();\r\n<\/code><\/pre>\n<p>This is not the way exported functions in ECMAScript are supposed to work when we call them.\nThat&#8217;s why TypeScript 4.4 intentionally discards the <code>this<\/code> value when calling imported functions, by using the following emit.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>\/\/ Imagine this is our imported module, and it has an export named 'foo'.\r\nlet fooModule = {\r\n    foo() {\r\n        console.log(this);\r\n    }\r\n};\r\n\r\n\/\/ Notice we're actually calling '(0, fooModule.foo)' now, which is subtly different.\r\n(0, fooModule.foo)();\r\n<\/code><\/pre>\n<p>You can <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44624\">read up more about the changes here<\/a>.<\/p>\n<h3 id=\"using-unknown-in-catch-variables\">Using <code>unknown<\/code> in Catch Variables<\/h3>\n<p>Users running with the <code>--strict<\/code> flag may see new errors around <code>catch<\/code> variables being <code>unknown<\/code>, especially if the existing code assumes only <code>Error<\/code> values have been caught.\nThis often results in error messages such as:<\/p>\n<pre class=\"language-text\" style=\"padding: 10px; border-radius: 10px;\"><code>Property 'message' does not exist on type 'unknown'.\r\nProperty 'name' does not exist on type 'unknown'.\r\nProperty 'stack' does not exist on type 'unknown'.\r\n<\/code><\/pre>\n<p>To get around this, you can specifically add runtime checks to ensure that the thrown type matches your expected type.\nOtherwise, you can just use a type assertion, add an explicit <code>: any<\/code> to your catch variable, or turn off <code>--useUnknownInCatchVariables<\/code>.<\/p>\n<h3 id=\"broader-always-truthy-promise-checks\">Broader Always-Truthy Promise Checks<\/h3>\n<p>In prior versions, TypeScript introduced &#8220;Always Truthy Promise checks&#8221; to catch code where an <code>await<\/code> may have been forgotten;\nhowever, the checks only applied to named declarations.\nThat meant that while this code would correctly receive an error&#8230;<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>async function foo(): Promise&lt;boolean&gt; {\r\n    return false;\r\n}\r\n\r\nasync function bar(): Promise&lt;string&gt; {\r\n    const fooResult = foo();\r\n    if (fooResult) {        \/\/ &lt;- error! :D\r\n        return \"true\";\r\n    }\r\n    return \"false\";\r\n}\r\n<\/code><\/pre>\n<p>&#8230;the following code would not.<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>async function foo(): Promise&lt;boolean&gt; {\r\n    return false;\r\n}\r\n\r\nasync function bar(): Promise&lt;string&gt; {\r\n    if (foo()) {            \/\/ &lt;- no error :(\r\n        return \"true\";\r\n    }\r\n    return \"false\";\r\n}\r\n<\/code><\/pre>\n<p>TypeScript 4.4 now flags both.\nFor more information, <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/pull\/44491\">read up on the original change<\/a>.<\/p>\n<h3 id=\"abstract-properties-do-not-allow-initializers\">Abstract Properties Do Not Allow Initializers<\/h3>\n<p>The following code is now an error because abstract properties may not have initializers:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>abstract class C {\r\n    abstract prop = 1;\r\n    \/\/       ~~~~\r\n    \/\/ Property 'prop' cannot have an initializer because it is marked abstract.\r\n}\r\n<\/code><\/pre>\n<p>Instead, you may only specify a type for the property:<\/p>\n<pre class=\"prettyprint language-typescript\" style=\"padding: 10px; border-radius: 10px;\"><code>abstract class C {\r\n    abstract prop: number;\r\n}\r\n<\/code><\/pre>\n<h2 id=\"whats-next\">What&#8217;s Next?<\/h2>\n<p>If you&#8217;re already looking to the future, you can <a href=\"https:\/\/github.com\/microsoft\/TypeScript\/issues\/45418\">take a peek at what we have in store for TypeScript 4.5<\/a>.\nIf you want to try it out, we always encourage users to <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/nightly-builds.html\">try and give feedback on our nightly builds<\/a> which tend to be very stable.<\/p>\n<p>Otherwise, we hope you&#8217;re excited to try out TypeScript 4.4, and we hope that it makes your 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 availability of TypeScript 4.4! If you haven&#8217;t heard of TypeScript yet, it&#8217;s a language that builds on JavaScript by adding syntax for static types. Tools like the TypeScript compiler just erase those types, leaving you with clean readable JavaScript that you can run anywhere; but those types are there [&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-3078","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 availability of TypeScript 4.4! If you haven&#8217;t heard of TypeScript yet, it&#8217;s a language that builds on JavaScript by adding syntax for static types. Tools like the TypeScript compiler just erase those types, leaving you with clean readable JavaScript that you can run anywhere; but those types are there [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/posts\/3078","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=3078"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/posts\/3078\/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=3078"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/categories?post=3078"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/typescript\/wp-json\/wp\/v2\/tags?post=3078"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}