Our Global Presence
Canada
57 Sherway St,
Stoney Creek, ON
L8J 0J3
India
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
USA
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
If you’re not familiar with TypeScript, it’s a language that builds on JavaScript by adding types and type-checking. Types can describe things like the shapes of our objects, how functions can be called, and whether a property can be null or undefined. TypeScript can check these types to make sure you’re not making mistakes in your programs so you can code with confidence. It can also power other tooling like auto-completion, go-to-definition, and refactorings in the editor. In fact, if you’ve used an editor like Visual Studio or VS Code for JavaScript, that same experience is already powered by TypeScript!
To get started with TypeScript 4.9, you can get it through NuGet, or use npm with the following command:
npm install -D typescript
You can also get editor support by
Here’s a quick list of what’s new in TypeScript 4.9!
Since the Release Candidate, no changes have been made to TypeScript 4.9.
TypeScript 4.9 beta originally included auto-accessors in classes, along with the performance improvements described below; however, these did not get documented in the 4.9 beta blog post.
Not originally shipped in the 4.9 beta were the new “Remove Unused Imports” and “Sort Imports” commands for editors, and new go-to-definition functionality on return keywords.
TypeScript developers are often faced with a dilemma: they want to ensure that some expression matches some type, but also want to keep the most specific type of that expression for inference purposes.
For example:
// Each property can be a string or an RGB tuple. const palette = { red: [255, 0, 0], green: "#00ff00", bleu: [0, 0, 255] // ^^^^ sacrebleu - we've made a typo! }; // We want to be able to use array methods on 'red'... const redComponent = palette.red.at(0); // or string methods on 'green'... const greenNormalized = palette.green.toUpperCase();
Notice that they’ve written bleu, whereas they probably should have written blue. They could try to catch that bleu typo by using a type annotation on palette, but they’d lose the information about each property.
type Colors = "red" | "green" | "blue"; type RGB = [red: number, green: number, blue: number]; const palette: Record<Colors, string | RGB> = { red: [255, 0, 0], green: "#00ff00", bleu: [0, 0, 255] // ~~~~ The typo is now correctly detected }; // But we now have an undesirable error here - 'palette.red' "could" be a string. const redComponent = palette.red.at(0);
The new satisfies operator lets us validate that the type of an expression matches some type, without changing the resulting type of that expression. As an example, you could use satisfies to validate that all the properties of palette are compatible with string | number[]:
type Colors = "red" | "green" | "blue"; type RGB = [red: number, green: number, blue: number]; const palette = { red: [255, 0, 0], green: "#00ff00", bleu: [0, 0, 255] // ~~~~ The typo is now caught! } satisfies Record<Colors, string | RGB>; // Both of these methods are still accessible! const redComponent = palette.red.at(0); const greenNormalized = palette.green.toUpperCase();
satisfies can be used to catch lots of possible errors. For example, they could ensure that an object has all the keys of some type, but no more:
type Colors = "red" | "green" | "blue"; // Ensure that we have exactly the keys from 'Colors'. const favoriteColors = { "red": "yes", "green": false, "blue": "kinda", "platypus": false // ~~~~~~~~~~ error - "platypus" was never listed in 'Colors'. } satisfies Record<Colors, unknown>; // All the information about the 'red', 'green', and 'blue' properties are retained. const g: boolean = favoriteColors.green;
Maybe they don’t care about if the property names match up somehow, but they do care about the types of each property. In that case, they can also ensure that all of an object’s property values conform to some type.
type RGB = [red: number, green: number, blue: number]; const palette = { red: [255, 0, 0], green: "#00ff00", blue: [0, 0] // ~~~~~~ error! } satisfies Record<string, string | RGB>; // Information about each property is still maintained. const redComponent = palette.red.at(0); const greenNormalized = palette.green.toUpperCase();
As developers, they often need to deal with values that aren’t fully known at runtime. In fact, they often don’t know if properties exist, whether they’re getting a response from a server or reading a configuration file. JavaScript’s in operator can check whether a property exists on an object.
Previously, TypeScript allowed us to narrow away any types that don’t explicitly list a property.
interface RGB { red: number; green: number; blue: number; } interface HSV { hue: number; saturation: number; value: number; } function setColor(color: RGB | HSV) { if ("hue" in color) { // 'color' now has the type HSV } // ... }
Here, the type RGB didn’t list the hue and got narrowed away, and leaving us with the type HSV.
But what about examples where no type listed a given property? In those cases, the language didn’t help us much. Let’s take the following example in JavaScript:
function tryGetPackageName(context) { const packageJSON = context.packageJSON; // Check to see if we have an object. if (packageJSON && typeof packageJSON === "object") { // Check to see if it has a string name property. if ("name" in packageJSON && typeof packageJSON.name === "string") { return packageJSON.name; } } return undefined; }
Rewriting this to canonical TypeScript would just be a matter of defining and using a type for context; however, picking a safe type like unknown for the packageJSON property would cause issues in older versions of TypeScript.
interface Context { packageJSON: unknown; } function tryGetPackageName(context: Context) { const packageJSON = context.packageJSON; // Check to see if we have an object. if (packageJSON && typeof packageJSON === "object") { // Check to see if it has a string name property. if ("name" in packageJSON && typeof packageJSON.name === "string") { // ~~~~ // error! Property 'name' does not exist on type 'object. return packageJSON.name; // ~~~~ // error! Property 'name' does not exist on type 'object. } } return undefined; }
This is because while the type of packageJSON was narrowed from unknown to object, the in operator strictly narrowed to types that actually defined the property being checked. As a result, the type of packageJSON remained object.
TypeScript 4.9 makes the in operator a little bit more powerful when narrowing types that don’t list the property at all. Instead of leaving them as-is, the language will intersect their types with Record<“property-key-being-checked”, unknown>.
So in our example, packageJSON will have its type narrowed from unknown to object to object & Record<“name”, unknown> That allows us to access packageJSON.name directly and narrow that independently.
interface Context { packageJSON: unknown; } function tryGetPackageName(context: Context): string | undefined { const packageJSON = context.packageJSON; // Check to see if we have an object. if (packageJSON && typeof packageJSON === "object") { // Check to see if it has a string name property. if ("name" in packageJSON && typeof packageJSON.name === "string") { // Just works! return packageJSON.name; } } return undefined; }
TypeScript 4.9 also tightens up a few checks around how in is used, ensuring that the left side is assignable to the type string | number | symbol, and the right side is assignable to object. This helps check that we’re using valid property keys, and not accidentally checking primitives.
TypeScript 4.9 supports an upcoming feature in ECMAScript called auto-accessors. Auto-accessors are declared just like properties on classes, except that they’re declared with the accessor keyword.
class Person { accessor name: string; constructor(name: string) { this.name = name; } }
Under the covers, these auto-accessors “de-sugar” to a get and set accessor with an unreachable private property.
class Person { #__name: string; get name() { return this.#__name; } set name(value: string) { this.#__name = name; } constructor(name: string) { this.name = name; } }
A major gotcha for JavaScript developers is checking against the value NaN using the built-in equality operators.
For some background, NaN is a special numeric value that stands for “Not a Number”. Nothing is ever equal to NaN – even NaN!
console.log(NaN == 0) // false console.log(NaN === 0) // false console.log(NaN == NaN) // false console.log(NaN === NaN) // false
But at least symmetrically everything is always not-equal to NaN.
console.log(NaN != 0) // true console.log(NaN !== 0) // true console.log(NaN != NaN) // true console.log(NaN !== NaN) // true
This technically isn’t a JavaScript-specific problem, since any language that contains IEEE-754 floats has the same behavior; but JavaScript’s primary numeric type is a floating point number, and number parsing in JavaScript can often result in NaN. In turn, checking against NaN ends up being fairly common, and the correct way to do so is to use Number.isNaN – but as we mentioned, lots of people accidentally end up checking with someValue === NaN instead.
TypeScript now errors on direct comparisons against NaN, and will suggest using some variation of Number.isNaN instead.
function validate(someValue: number) { return someValue !== NaN; // ~~~~~~~~~~~~~~~~~ // error: This condition will always return 'true'. // Did you mean '!Number.isNaN(someValue)'? }
They believe that this change should strictly help catch beginner errors, similar to how TypeScript currently issues errors on comparisons against object and array literals.
In earlier versions, TypeScript leaned heavily on polling for watching individual files. Using a polling strategy meant checking the state of a file periodically for updates. On Node.js, fs.watchFile is the built-in way to get a polling file-watcher. While polling tends to be more predictable across platforms and file systems, it means that your CPU has to periodically get interrupted and check for updates to the file, even when nothing’s changed. For a few dozen files, this might not be noticeable; but on a bigger project with lots of files – or lots of files in node_modules – this can become a resource hog.
Generally speaking, a better approach is to use file system events. Instead of polling, they can announce that they’re interested in updates of specific files and provide a callback for when those files actually do change. Most modern platforms in use provide facilities and APIs like CreateIoCompletionPort, kqueue, epoll, and inotify. Node.js mostly abstracts these away by providing fs.watch. File system events usually work great, but there are lots of caveats to using them, and in turn, to using the fs.watch API. A watcher needs to be careful to consider inode watching, unavailability on certain file systems (e.g.networked file systems), whether recursive file watching is available, whether directory renames trigger events, and even file watcher exhaustion! In other words, it’s not quite a free lunch, especially if you’re looking for something cross-platform.
As a result, their default was to pick the lowest common denominator: polling. Not always, but most of the time.
Over time, they’ve provided the means to choose other file-watching strategies. This allowed us to get feedback and harden our file-watching implementation against most of these platform-specific gotchas. As TypeScript has needed to scale to larger codebases, and has improved in this area, we felt swapping to file system events as the default would be a worthwhile investment.
In TypeScript 4.9, file watching is powered by file system events by default, only falling back to polling if we fail to set up event-based watchers. For most developers, this should provide a much less resource-intensive experience when running in –watch mode, or running with a TypeScript-powered editor like Visual Studio or VS Code.
The way file-watching works can still be configured through environment variables and watchOptions – and some editors like VS Code can support watchOptions independently. Developers using more exotic set-ups where source code resides on a networked file systems (like NFS and SMB) may need to opt back into the older behavior; though if a server has reasonable processing power, it might just be better to enable SSH and run TypeScript remotely so that it has direct local file access. VS Code has plenty of remote extensions to make this easier.
Previously, TypeScript only supported two editor commands to manage imports. For our examples, take the following code:
import { Zebra, Moose, HoneyBadger } from "./zoo"; import { foo, bar } from "./helper"; let x: Moose | HoneyBadger = foo();
The first was called “Organize Imports” which would remove unused imports, and then sort the remaining ones. It would rewrite that file to look like this one:
import { foo } from "./helper"; import { HoneyBadger, Moose } from "./zoo"; let x: Moose | HoneyBadger = foo();
In TypeScript 4.3, they introduced a command called “Sort Imports” which would only sort imports in the file, but not remove them – and would rewrite the file like this.
import { bar, foo } from "./helper"; import { HoneyBadger, Moose, Zebra } from "./zoo"; let x: Moose | HoneyBadger = foo();
The caveat with “Sort Imports” was that in Visual Studio Code, this feature was only available as an on-save command – not as a manually triggerable command.
TypeScript 4.9 adds the other half, and now provides “Remove Unused Imports”. TypeScript will now remove unused import names and statements, but will otherwise leave the relative ordering alone.
import { Moose, HoneyBadger } from "./zoo"; import { foo } from "./helper"; let x: Moose | HoneyBadger = foo();
This feature is available to all editors that wish to use either command; but notably, Visual Studio Code (1.73 and later) will have support built in and will surface these commands via its Command Palette. Users who prefer to use the more granular “Remove Unused Imports” or “Sort Imports” commands should be able to reassign the “Organize Imports” key combination to them if desired.
In the editor, when running a go-to-definition on the return keyword, TypeScript will now jump you to the top of the corresponding function. This can be helpful to get a quick sense of which function a return belongs to.
They expect TypeScript will expand this functionality to more keywords such as await and yield or switch, case, and default.
For more information and to develop web applications using TypeScript, Hire TypeScript Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”.
To develop custom web apps using TypeScript, please visit our technology page.
Content Source:
57 Sherway St,
Stoney Creek, ON
L8J 0J3
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
57 Sherway St,
Stoney Creek, ON
L8J 0J3
606, Suvas Scala,
S P Ring Road, Nikol,
Ahmedabad 380049
1131 Baycrest Drive,
Wesley Chapel,
FL 33544
© 2024 — HK Infosoft. All Rights Reserved.
© 2024 — HK Infosoft. All Rights Reserved.
T&C | Privacy Policy | Sitemap