Type Checking In TypeScript: A Beginners Guide

Jayson Lennon

Jayson Lennon

hero image

In This Guide:

What are types and what is type checking, type aliases, type predicates, go test these out with your own code.

There's nothing worse than writing code, and then having it break due to a simple error.

code errors

The good news is that Typescript has several type checking techniques built-in, that enable us to catch errors at both compile-time and at runtime .

This not only makes our lives so much easier but also helps to enhance code quality and build more robust and maintainable applications.

It's a win:win situation.

The trick, of course, is knowing how to use these type checking features, and so that's what I'm going to show you today.

Sidenote: I'm going to assume if you're reading this article that you have some experience with either TypeScript or JavaScript .

However, if you struggle to understand some of the concepts covered here, how to apply them, or just want a deep dive into TypeScript, then check out my complete course on Typescript here .

learn typescript

It takes you from complete beginner, to building real-world applications and being able to get hired, so there's something for any level of TypeScript user.

Check it out above, or watch the first videos here for free .

With that out of the way, let's get into type checking and get everyone up to speed with some basics.

Types and type checking in TypeScript is basically a way of getting around any issues that you might usually get with dynamic JavaScript.

Here's how it works.

Every piece of data in TypeScript is given a "type", and this "type" determines what properties the data has, as well as what methods are available to it.

These types can be:

  • Type assertions

The types that are assigned to data and functions are done in code through various mechanisms, but we'll cover that later.

Type checking

Because all of the data in TypeScript is given a type, this means that the compiler can check every property of that data to make sure it's being used correctly .

This is called type checking and it includes:

  • Making sure that the expected properties exist, and
  • That the methods and functions being used are all compatible

Pretty handy, but it gets better.

Because of these types in the code, it means that TypeScript can perform most of the type checking at compile time , giving you immediate feedback in your IDE, and a huge productivity boost!

typechecking benefits

When TypeScript uses types to perform compile time type checking, those types are classified as static types . However, there are different techniques to interface with TypeScript for varying degrees of type checking.

Some type checking is simple, such as checking if a section of data is a number or not. Other type checking can be more complicated, but it also allows for more complexity in rules.

For example

You could set it up so that you only allow logged-in users who are employees of the customer service department to gain access to a specific web page.

I'll go through this more as we work through this guide, but typically, more guarantees and complexity will require more effort and more code. That being said, you can still get substantial productivity gains and reduction in bugs with even just a small amount of type checking .

So let's look at a few type checking methods.

Compile time type checks

The first (and most important) stage for type checking in TypeScript are compile time type checks.

Here's how compile time checks work:

  • The compiler will analyze all the types used in your code and make sure the code is correctly using those types
  • If some code uses a type incorrectly, then a compiler error will occur and you'll know immediately that something is wrong
  • If the program doesn't pass compile time type checking, then chances are it won't run correctly

In order to leverage compile time type checking, it's important to understand some of the TypeScript techniques available for interfacing with the type checker.

We won't go into detail about all of the type checking techniques available, as that can get a little overwhelming. Instead, let's take a look at a sample of techniques, just so you can get an idea of what's possible.

Type aliases provide a way to give a name to some type, and are usually used to make code easier to read and to work with.

This can be something simple like assigning a new name to an existing type, or it can be more complex like defining the shape of an object.

Imagine that we have a list of grocery items, like so:

This code works, and TypeScript will guarantee that we have an array of tuples containing both string and number .

However, a problem occurs in that we're not exactly clear on what data we need for the grocery list .

The string could be a name like "tomato" , but it could also be an ID like "P-51321" , whereas number is probably the quantity, but what if it's the price instead?

unclear code

Pretty confusing right?

The good news is that we can improve this code by using type aliases to make it clear what data is required for this array of tuples.

Now we can see that groceries should consist of a Name and a Price , and using this code won't leave us scratching our heads trying to figure out what to put in the string or number sections.

Type aliases for shape definition

As mentioned earlier, type aliases can also be used for defining the shape of objects.

With that in mind, we could create a type alias to define what a grocery item consists of:

Now that we have this alias, TypeScript will ensure that anytime we use the alias in our program, the data it refers to will always have the three properties we defined.

This has some implications:

  • We don't need to check if the properties are present in the object, because they are guaranteed to exist
  • The properties will always contain the data we defined , so we don't need to check the data in each property
  • We can write functions and just assume that the data exists and is the correct type , because TypeScript guarantees these
  • The program will have better performance because we don't need to check anything at runtime
  • The code is easier to work with because we can focus on implementation and just let TypeScript give us an error if we make a mistake

As you can see, this is all incredibly helpful.

Here is how we can use our GroceryItem alias in a function:

There's also other benefits to this method.

Added benefit: Type aliases help you to 'auto' document

I don't know about you but sometimes when you're writing functions, it feels like it should be obvious what kind of data should be passed to it, and what it should return.

However, that's only because we have knowledge of how it works, since we are writing it . The reality though, is that we'll probably forget about it in another hour or two as we move on to other things.

However, we can get around this when using type aliases.

Because using type aliases for function parameters and return types can help indicate what data is required for the function, and what it returns , while also acting as a form of self-documenting code .

For example:

No more trying to remember what data is needed!

However, aliases are not a perfect method for type checking...

The problem with aliases in type checking

Type aliases can make code significantly clearer, but there is one caveat when using type aliases, and it's that they are just aliases , and not a unique type.

This means that it's possible to accidentally mix up some data that passes type checking, but produces incorrect program behavior.

Imagine that we have a function that prints out a full name from first and last name parts:

See what happened there? It switched the first and last name around.

However, even though this is the wrong order and a clear bug, TypeScript doesn't give us any type checking errors. This is because in theory, the types are correct, and so the program runs without crashing.

Obviously this isn't what we want, and type aliases aren't able to fix this issue because a string is always a string , regardless of how many aliases we create.

strings are strings

However, TypeScript is perfectly capable of making this bug impossible, we just need to tweak our code to use a different type.

Classes enable us to give a name to some data which is checked at compile time, similar to a type alias , but with one critical difference in that classes are their own unique type .

This means that if we used classes instead of type aliases in the printName function from the previous section, then we would get a compiler error when we mixed up the first and last names.

Let's take a look at a revised version that uses classes instead:

Important: Setting the class properties to private is a vital step here, because TypeScript uses a structural type system .

This means that TypeScript looks at the shape of the data it has and compares it to the shape of the data it needs.

Since both classes have a name property, TypeScript would correctly identify that they have the same shape, if the properties were public . But by making the properties private , it hides them, so TypeScript isn't able to compare the shape of FirstName and LastName .

This is exactly what we need because we want the type checking to fail if we swap the first name and last name accidentally.

Usage is similar to the previous example with aliases, except we need to instantiate the classes:

Boom! No more reverse name order.

Classes have many other features outside the scope of compile time type checks, so definitely check out the official handbook to learn more about their capabilities. ( I also cover them more in my course content here .)

Interfaces offer a way to define the shape of data, similar to type aliases while also allowing you to declare the methods that must exist, like classes .

However, the actual implementation of the methods must be done on a class, as the interface declares only the signature of the methods and not the body.

The shape and methods of interface implementations are checked at compile time, so whatever data is using an interface will always have the correct shape and have the specified methods available.

We can take advantage of these facts by writing functions that operate on interfaces which frees us from having to perform any runtime checks.

We can create a Logger interface that specifies that a log method exists:

This log method has no implementation in the interface, so it's up to a class to implement it.

We can then create multiple implementations that use this interface and each one will be compatible with other code that uses the log method:

Now that we have some implementations of the Logger interface, we can use both of them with the same function, like so:

Boom! If we tried to use writeLog with a class that doesn't implement the Logger interface, then the compile time type checking would fail and we wouldn't be able to compile our program.

Using interfaces provide strong type checking at compile time similar to classes, while also having a bit more flexibility similar to type aliases.

Runtime type checks

Not all situations are capable of being analyzed at compile time, so TypeScript has runtime type checking to handle this.

runtime checks

These checks are useful when you are working with data from an untrusted source, like an external API, and you need to check the data to ensure that it conforms to the types in your code.

Runtime type checking can also be used when you have multiple possible types in a union , but you don't know which you are working with until the program runs.

The most effective way to make use of runtime type checking is to create a multi-staged process that takes some data at runtime and assigns it to a known static type.

The process is broken into 3 steps:

  • Receive some data at runtime
  • Perform a runtime type check to assign it to a single static type
  • Use the now known static type throughout your program

Step 3 is when compile time type checking takes over. Once the type is a known static type, it can then be used throughout the remainder of the program with support of the TypeScript compiler and IDE auto-completions.

Let's take a look at what options are available to perform runtime type checking.

The typeof operator provides basic support for performing a runtime type check.

However, it's a JavaScript operator, so it only operates on primitive JavaScript types. This means we can't use it directly to figure out a TypeScript type, but we can combine it with the methods discussed later in this post to get a TypeScript type .

Here is an example of how to use typeof :

The instanceof operator gets us a little closer to identifying the types we are working with, as it can be used to figure out which class some data was instantiated from:

But, since instanceof only applies to classes, we won't be able to use it with interfaces or type aliases.

typeof and instanceof only allow us to branch based on some criteria, and while it's technically possible to move the data around and eventually arrive at a static TypeScript type, it takes a lot of code and is difficult to maintain.

This is where type predicates come in.

Type predicates allow us to assign some runtime data to a static type. Once assigned, we get the benefits of TypeScript's compile time type checking.

In this example, we will use a news site that has blog posts and news articles:

To create a type predicate to determine which interface we are working with, we use the is keyword as part of the return type of a function.

This keyword tells TypeScript whether or not the input data is of a certain type:

The type predicates return a boolean that indicates whether the data is the type we expect.

  • If the function returns true , then TypeScript will use the type indicated in the predicate, and we get the benefits of compile time type checking and IDE completions
  • If it's false , then TypeScript can't determine the correct type, and so it won't be able to perform compile time checking

Here is the remainder of the program that utilizes the type predicate:

There are no restrictions on what kinds of data we can use type predicates with, and we can write them to operate on the any type, or on a union of types.

The goal is the same: narrow down a large set of types to just a single type which we can then use in the program.

As you can see, there are many more techniques available for compile time and runtime type checking in TypeScript, and each are incredibly helpful for reducing time spent looking for errors.

We've only covered the tip of the iceberg here, but I hope this small selection helped to illustrate the differences between the both compile and runtime checks, and gave you an idea of what's possible in TypeScript.

If you want to learn more, check out my complete TypeScript course here on Zero To Mastery, or watch the first videos for free .

Like I said earlier, it can take you from a total beginner with basic JS experience, to building large scale apps and being able to be hired as a TypeScript developer.

You'll not only get the course content, you'll also get direct access to me, as well as other devs who are all learning TS, in our private Discord server.

TypeScript Bootcamp: Zero to Mastery

TypeScript Bootcamp: Zero to Mastery

Learn TypeScript from scratch by building your own real-world apps. This course is for developers serious about taking their coding skills and career to the next level.

Build a Notion Clone with React and TypeScript

Build a Notion Clone with React and TypeScript

Take your skills to the next level by building a React Typescript project! This course will teach how to use TypeScript with React while building a Notion Clone using best practices.

Build an Instagram Filters App with Vue, TypeScript and WebAssembly

Build an Instagram Filters App with Vue, TypeScript and WebAssembly

Take your Vue skills to the next level and build a portfolio project! This course will teach you to build an Instagram filters app using best practices and help you overcome common mistakes made by Vue Developers.

More from Zero To Mastery

How To Ace The Coding Interview preview

Are you ready to apply for & land a coding job but not sure how? This coding interview guide will show you how it works, how to study, what to learn & more!

ZTM Instructor Team

This is Part 3 of a 3 part series on how I taught myself programming, and ended up with multiple job offers in less than 6 months!

Andrei Neagoie

You DO NOT need a CS Degree to get hired as a Developer. Learn Computer Sciences Basics today with this free guide by a Senior Dev with 10+ years of experience.

Yihua Zhang

TypeScript: Mastering Type Assignment and Type Inference

TypeScript introduced static typing, enabling developers to specify the types of variables, function parameters, and return values.

typescript-type-assignment-and-type-inference

TypeScript, a superset of JavaScript, introduces static typing, enabling developers to specify the types of variables, function parameters, and return values. This capability significantly enhances code quality and developer productivity by catching errors early in the development process. Two fundamental concepts in TypeScript's type system are type assignment and type inference. Understanding these concepts is crucial for leveraging TypeScript's full potential to create robust and error-resistant applications.

Type Assignment

Type assignment in TypeScript is straightforward: it allows developers to explicitly specify the type of a variable, function parameter, or return value. This explicit typing helps catch type-related errors during compilation, long before the code is executed.

Syntax and Usage

Type assignment is done using a colon ( : ) followed by the type. Here are a few examples:

In these examples, userName is explicitly declared as a string , isLoggedIn as a boolean , and userAge as a number . The greet function expects a string parameter and is also expected to return a string .

Type Inference

Type inference allows TypeScript to automatically deduce the types of variables and expressions when they are not explicitly provided. This feature simplifies the code by reducing the verbosity of type annotations, without sacrificing the benefits of static typing.

How Type Inference Works

TypeScript's type inference comes into play in several scenarios, such as variable initialization and default function parameters. Here's an example:

In these cases, TypeScript infers the types based on the initial values. userId is inferred to be a number , isAdmin a boolean , and the multiply function's return type is inferred as number .

Best Practices for Type Assignment and Inference

While TypeScript's type inference is powerful, relying solely on inference can sometimes lead to less readable code, especially in complex scenarios. Here are some best practices:

  • Use explicit types for public API boundaries : This includes exported functions, classes, and interfaces. Explicit types improve documentation and make your code easier to understand for other developers.
  • Leverage type inference for local variables : For simple, internal code blocks, let TypeScript infer the types of local variables to reduce verbosity.
  • Prefer const over let for immutable values : This makes your intention clear and allows TypeScript to infer the type more accurately.

TypeScript's type assignment and type inference are two pillars that support writing clear, error-free code. By combining explicit type annotations with TypeScript's intelligent type inference, developers can enjoy the best of both worlds: the flexibility of JavaScript with the robustness of a statically-typed language. Whether you're a seasoned TypeScript developer or just starting out, mastering these concepts is key to unlocking the full power of TypeScript in your projects.

For those looking to dive deeper into TypeScript and explore its vast capabilities, PullTheCode offers a wealth of resources, tutorials, and best practices. From type assignment and inference to advanced types and utility types, PullTheCode is your go-to destination for elevating your TypeScript skills.

Own your own Software!

Join PullTheCode and start building your own SaaS in minutes on a platform you own!

Coming from a C# background, I don’t tend to use Discriminated Unions a whole awful lot. By Discriminated Unions, I mean having a method that may take multiple different types, or may return multiple different types.

If you’ve come from a C# world, it’s basically like method overloads when passing parameters into a method. And the other way, it allows you to make a method that could return one of many different types, that don’t necessarily have to have an inheritance relationship between them.

Let’s see how this might work :

It’s somewhat strange at first to get used to this ability if you’ve never used a language that uses unions. Most developers would instead make a “base” class of “Vehicle” and make both Plane and Car inherit from Vehicle, and then you don’t need a union. Which.. In my view is probably valid.

But lets say you can’t do that, and you are dealing with code that either returns a Plane or Car, *or* code that accepts a Plane or Car. You’re going to need to know at some point, which one you have. Because if the objects were identical, you probably wouldn’t need this union at all. Type checking in Typescript on the surface seemed easy, but I went down a bit of a rabbit hole through documentation and guides that weren’t always clear. So I want to try and simplify it down all right here, right now.

Using The TypeOf Operator

Javascript actually has a typeof operator itself that can tell you which type a variable is. As an example, we can do things like :

But.. This isn’t as helpful as you might think. Other than the fact that it returns the “type” as a string, which is rather unhelpful in of itself, it doesn’t work with complex types. For example :

For all custom classes (Which, in modern JavaScript you will have many), the return type is only ever object. That’s because the typeof operator can only tell you which primitive type your variable is, but nothing beyond that.

For this reason, while it may be helpful for telling strings from numbers, anything more, typeof is out!

Using The InstanceOf Operator

That brings us to the instanceof operator. It’s actually rather simple! We can just change our code to work like so :

Works well and we can now tell if our variable is an instance of a car. But there is a caveat, and it’s around inheritance. Consider the following code :

Notice that even though our variable holds a “Honda”, it still returns true as a car. For the most part, this is how all programming languages work so we shouldn’t read too much into this “limitation” as it’s really just polymorphism at play, but it’s still something to keep in mind.

Alas, we have an issue! A really smart developer has come along and said that interfaces are the new hip thing, and we should switch all classes to interfaces in our front end code. So we have this :

This time around, we don’t even get to run our code and instead we get :

What’s going on here? Well.. the instanceof operator works with classes only, not interfaces. Gah! OK but actually there is a way to check this further!

Using The As Cast Operator

Consider the following code (And yes I know it’s a fairly verbose example, but should hopefully make sense!)

Notice how we can cast our variable to a Car, and check if it has a value (by using a truthy statement, it will be undefined otherwise). Casting is actually a great way to determine whether variables are instances of an interface.

Using Typescript Type Guards

One thing I want to point out about the above code is that we have had to actually cast the car twice. Notice that inside the console log, we had to cast again for intellisense to pick up we were using a Car.

Typescript has a way to deal with this however. It’s called “Type Guards”, and it allows you to write code that will not only check an object is a given type, but that Typescript from that point on can treat the variable as the type.

For example, we can create a custom type guard like so :

The magic sauce here is the return type. It’s actually “car is Car”. That tells Typescript that should this return true, the input variable can be treated as a Car from this point onwards. This allows us to change our code to instead be :

Notice how inside our console log, we didn’t have to cast again to have it act as a car.

This is a really important concept when talking about type guards. It’s not just a way to create a nice fancy method that can be used to check types, it actually allows Typescript to start treating what could be an unknown object type, as a very specific one within that code block.

Related Posts:

  • What is the any type in TypeScript?
  • What is the unknown type in TypeScript?
  • What Are Type Aliases in TypeScript?
  • What is Type Casting in TypeScript?
  • Type Aliases vs Interfaces in TypeScript
  • How to Handle and Convert the 'Unknown' Type in TypeScript

👋 Hey, I'm Wade

💬 leave a comment cancel reply.

Your email address will not be published. Required fields are marked *

Upmostly brings you original JavaScript framework tutorials every week.

  • Cheatsheets
  • Write For Us

Copyright © 2024 Upmostly

Advisory boards aren’t only for executives. Join the LogRocket Content Advisory Board today →

LogRocket blog logo

  • Product Management
  • Solve User-Reported Issues
  • Find Issues Faster
  • Optimize Conversion and Adoption
  • Start Monitoring for Free

Improving TypeScript error handling with exhaustive type checking

typescript check type before assignment

There are very few programs that work in complete isolation. Even if developers always write perfect code, there is a high likelihood of encountering errors when code interacts with external components like databases, REST APIs, and even that trendy npm package that has a bunch of stars!

Improving Error Handling Typescript Exhaustive Type Checking

Instead of sitting back while the server crashes, a responsible developer thinks defensively and prepares for when a malformed request comes in. In this article, we’ll cover a few of the most common approaches for error handling in TypeScript (TS). We’ll talk about most common error types, the shortcomings of null and try...catch based error handling, and finally, propose a cleaner way to manage errors.

Let’s get started!

Note: If you’re not familiar with TypeScript, good news. The conditions that cause errors and the solutions also apply to JavaScript.

Known errors

Programmer errors, returning null, throwing custom errors using try...catch, adding exhaustive type checking, let’s talk errors.

​​To effectively discuss and evaluate the different error handling approaches in TypeScript, it’s essential to first talk about the nature of errors. ​​ ​​Not all errors are created equal, and we can sort them into two main buckets: known errors​ and programmer errors​.

Known errors​ are the errors that we know will happen (eventually): a DB connection fails, an HTTP call returns an error code, the user inputs an invalid email in a form… All these errors have in common that while developing, the developer is aware of them. When they happen, they are dealt with, and it’s possible to try to recover (for example, by retrying the HTTP call). Failing to deal with these errors correctly creates programmer errors​ (aka bugs).

​​Programmer errors​ (bugs) are uncaught known errors. If an array has a length of 10 and we try to access the element number 11, it will generate an exception which can bring the runtime down. Bugs are not known beforehand (or they would be dealt with and become known errors​), so they can’t be planned for. Unhandled out-of-memory and other environment errors also are considered programmer errors​, so a graceful recovery from them is often not possible. ​​ ​​It’s necessary to keep this distinction in mind since we will be addressing known errors​ in the remainder of the post. By definition, programmer errors​ are not known beforehand, so they shouldn’t be treated the same as known errors​. We will expand on the dangers of mixing error types in the next section, so keep reading! ​📚​

Popular TypeScript error handling approaches

Before we dig in, keep in mind that the following list is by no means exhaustive. The errors and solutions presented here are based on my subjective experience, so your mileage may vary. 🏎️

Returning null is a common way to indicate that something in your code went wrong. It is best used when a function only has one way to fail, however, some developers use it when a function has many errors.

Returning null in TypeScript forces null checks everywhere in your code, causing the specific information about the cause of the error to be lost. Returning null is an arbitrary representation of an error, so if you try returning 0 , -1 , or false , you’ll end up with the same result.

In the code block below, we’ll write a function that retrieves data about the temperature and humidity of a given city. The getWeather function interacts with two external APIs through two functions, externalTemperatureAPI and externalHumidityAPI , and aggregates the results:

We can see that on entering Berlin , we receive the error messages Error fetching temperature for ${city} and Error fetching humidity for ${city} .

Both of our external API functions can fail, so getWeather is forced to check for null for both functions. Although checking for null is better than not handling errors at all, it forces the caller to make some guesses. If a function is extended to support a new error, the caller won’t know it unless they check the inners of the function.

Let’s say that externalTemperatureAPI initially throws a null when the temperature API returns HTTP code 500 , which indicates an internal server error. If we extend our function to check the structural and type validity of the API response (i.e., check that the response is of type number ), the caller will not know if the function returns null due to HTTP code 500 or an unexpected API response structure.

typescript check type before assignment

Over 200k developers use LogRocket to create better digital experiences

typescript check type before assignment

Creating custom errors and throwing them is a better option than returning null because we can achieve error granularity, which enables a function to throw distinct errors and allows the caller of the function to handle the distinct errors separately.

However, the execution of any function that throws an error will be halted and the error propagated up, disrupting the regular flow of the code. Although this may not seem like a big deal, especially in small applications, as your code continues to layer try...catch after try...catch , readability and overall performance will decline.

Let’s try to solve the error in our weather example with the try...catch method:

In the code block above, when we try to access the externalTemperatureAPI and the externalHumidityAPI , we are met with two errors in the console.log , which are then stopped and propagated up several times.

We have discussed the shortcomings of try...catch regarding not knowing if a function may go through and thus polluting the codebase with unnecessary checks. That’s quite a bummer, but try...catch has another spooky surprise for us. 🎃

Remember the distinction between known and programmer errors ? try...catch catches ALL of the errors, mixing them up. This is dangerous since bugs get flagged as known errors , triggering unexpected side effects and making debugging the original issue much harder:

But don’t despair, let’s see how we can avoid mixing the two error types!

The Result class

When you use either of the two error handling approaches discussed above, a simple mistake can add unnecessary complexity on top of the original error. The problems that arise when returning null and throwing a try...catch are shared in other frontend languages like Kotlin , Rust, and C#. These three languages use the Result class as a fairly generalized solution.

Regardless of whether execution succeeds or fails, the Result class will encapsulate the results of the given function, allowing the function callers to handle errors as part of the normal execution flow instead of as an exception.

When paired with TypeScript, the Result class provides type safety and detailed information about the possible errors that a function could result in. When we modify the error results of a function, the Result class provides us with compile-time errors in the affected places of our codebase.

Let’s look back at our weather example. We’ll use a TypeScript implementation of Rust’s Result and Option objects, ts-results :

There are other packages for TypeScript with very similar APIs, like NeverThrow , so you should feel free to play around.

Adding type-safe results from the function and prioritizing error handling in our code is an improvement from our previous examples. However, we still have work to do. Let’s explore how we can make our type checks exhaustive.

It is important to note that favoring the Result class doesn’t mean that you won’t use try...catch structures.  try...catch structures are still required when you are working with external packages.

If you think the Result class is worth following, you can try encapsulating those touchpoints in modules and using the Result class internally.

When handing functions that can return multiple errors, it can be helpful to provide type checks for covering all error cases. Doing so ensures that the caller of the function can react dynamically to the type of error, and it provides certainty that no error case is overlooked.

We can achieve this with an exhaustive switch:

Running our weather example with the exhaustive switch will provide compile-time errors under two sets of circumstances. One is when all error cases are not handled, and the other is when the errors in the original function change.

Now, you know an improved solution for handling common errors in TypeScript! Knowing how important error handling is, I hope you’ll use this method to get the most specific information about any errors in your application.

In this tutorial, we covered the downsides of some widespread approaches like returning null and the try...catch method. Finally, we learned how to use the TypeScript Result class with an exhaustive switch for error catching.

LogRocket : Full visibility into your web and mobile apps

LogRocket Dashboard Free Trial Banner

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

Try it for free .

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • #typescript

typescript check type before assignment

Stop guessing about your digital experience with LogRocket

Recent posts:.

How To Integrate WunderGraph With Your Frontend Application

How to integrate WunderGraph with your frontend application

Unify and simplify APIs using WunderGraph to integrate REST, GraphQL, and databases in a single endpoint.

typescript check type before assignment

Understanding the latest Webkit features in Safari 17.4

The Safari 17.4 update brought in many modern features and bug fixes. Explore the major development-specific updates you should be aware of.

typescript check type before assignment

Using WebRTC to implement P2P video streaming

Explore one of WebRTC’s major use cases in this step-by-step tutorial: live peer-to-peer audio and video streaming between systems.

typescript check type before assignment

htmx vs. React: Choosing the right library for your project

Both htmx and React provide powerful tools for building web apps, but in different ways that are suited to different types of projects.

typescript check type before assignment

3 Replies to "Improving TypeScript error handling with exhaustive type checking"

There are 3 issues with what’s being proposed in this article:

1. Having too many try/catch blocks everywhere isn’t necessary, and is usually a code smell. Errors need to be caught usually only when we need to “wrap” an error (i.e. bubble up a different kind of error type or message) and once at the top level as a generic way to catch unhandled errors and alert the user in a friendlier way than crashing the app.

2. The “it catches all the errors without distinction” argument is only a problem if you’re not checking the error type in the catch block. If you have distinct types of each error, distinguishing between them is as simple as doing `if (error instanceof MyCustomErrorType)` to check for it and doing something, and just doing `else throw error` if it’s any other error to bubble it up.

3. The “Result” pattern is fine in other languages like Rust or Go where it’s a first-class citizen of the language, but it doesn’t make sense in TypeScript/JavaScript simply because “Result” types aren’t a native construct and starts feeling unnatural and verbose after a while, when just knowing how to throw and catch errors properly works just fine, is less verbose, and is tightly integrated into the language.

The `Result` pattern is definitely not the only way, and in small or simple apps try/catch may be enough.

In order to effectively check the error types, you have to manually go into all the functions that are called in a given try block (and all the nested functions). A project-wide error convention may mitigate this, but it doesn’t solve the issue of needing to perform manual checks. By using the pattern, all the returns of a function are typed, not only the happy path.

At the end of the day, this pattern makes the error flows explicit and typed, which is not supported by the built in try/catch. Verbosity is a subjective topic, there are plenty of libraries that expose a terser API: https://github.com/swan-io/boxed

Finally, this pattern is already in use by built in JS APIs, such as the Fetch API (using the Response class). The post adds extra ergonomics by leveraging TypeScript.

Some false examples in the demo code.

Rule: Only throw an exception if the method can not handle the error.

If the method can handle the error, great; else, allow the exception to naturally go up the chain until something can address the problem.

Rule: Never double-log an error/exception.

Double-logging is a common problem in event management where the same error is seen all through the logs. The only code that should be logging the problem is the code that deals with the exception.

Conclusion: Following the basic rules will clean up the code and the log files and eliminate the need for the Result class/data.

Leave a Reply Cancel reply

DEV Community

DEV Community

Sam Jones

Posted on Jul 30, 2021

Run a TypeScript type check in your pre-commit hook using lint-staged + husky

A great way to prevent TypeScript compilation errors from bringing down your CI pipelines is to introduce a type check before you commit your .ts file changes.

If you've worked with Git Hooks before, you'll probably know that one of the best combinations for running pre-commit checks is husky and lint-staged .

Together, these two packages allow you to tap into the relevant Git hook and run commands such as linting on staged files only . It saves a bunch of debugging time by fixing preventable errors sooner in the development process.

As a separate step, if you want to check for TypeScript type errors (note: this is different to syntax errors which ESLint picks up and fixes for you), you would typically run a command in the CLI such as npx tsc --noEmit to compile the TypeScript and highlight any type errors to address.

Now, the best case scenario here is that you simply pop the above TypeScript command into your lint-staged config along with anything else e.g. ESLint, and you're good to go!

Oh no, it doesn't quite work as expected. The error message that you may come across looks something like this:

The issue I found - and at the time of writing, it is still being discussed - is that lint-staged would pass each staged file to the npx tsc command like npx tsc --noEmit file1.ts file2.ts and that causes TypeScript to simply ignore your tsconfig.json .

Super frustrating!

Fear not however, as there is an extremely helpful tool that runs tsc on specific files without ignoring tsconfig.json and it's called tsc-files . As the tool's author points out:

I wanted to type-check only the staged files with lint-staged.

Perfect for my use case. So, after a quick look at the docs, it was as simple as updating the lint-staged config in my package.json file to use the tsc-files package:

Now I am able to run a TypeScript type check on all my staged files using a pre-commit hook.

Tip: use the --pretty flag in the tsc command to add some formatting and colour to your type errors.

If you have any questions on this setup or have tried different approaches, feel free to comment below or start up a convo over on Twitter .

Thanks for reading 👋

Top comments (10)

pic

Templates let you quickly answer FAQs or store snippets for re-use.

fatedx profile image

  • Joined Mar 24, 2017

@martinsjastad, I read your article a few months ago and was all hyped on tsc-files. I set it up and used it in Production until today when I came to the hard conclusion that it simply does not help identify errors in other files .

So, for example, if you change the way an export works in FileA and commit your code, any files importing FileA will likely all be broken, and you will not be notified of the breakage until you deploy it.

It is for this reason that I instead added a Husky pre-commit hook to my project that executes npx tsc on commit and fails the commit if any errors are found.

I hope this helps someone. :)

thiagocolebrusco profile image

I was looking for something like lint-staged but then I thought exactly the same thing. It is not helpful for validating TS.

luckydevboy profile image

  • Email [email protected]
  • Location Tehran, Iran
  • Education Industrial Engineering
  • Work Front-End Developer
  • Joined Oct 10, 2020

Can you explain how did you do that?

skathuria29 profile image

  • Location New Delhi, India
  • Work Senior Software Engineer at Adobe
  • Joined Mar 25, 2019

Hello @fatedx so by this, your pre commit hook runs on all files be it staged or unstaged ? In order to catch that import error.

amiceli profile image

  • Location France
  • Joined Feb 12, 2020

@samueldjones you made my day ! I added it to lint-staged config (custom d.ts file for import with files like .vue ;) ) and it works ! Really thanks you.

mrms profile image

  • Joined Jul 10, 2018

Would this check the types of other files affected by changes to the staged files?

samueldjones profile image

  • Location Manchester, UK
  • Work Senior Frontend Developer at VictorianPlumbing.co.uk
  • Joined Aug 4, 2020

Hmm I don't think it would as it specifically looks at staged - worth investigating though!

thomassalty profile image

  • Joined Dec 27, 2019

What if there are let's say 10 staged *.ts files and all of them have type errors? Will this solution type check all 10 files or throws an error after the first one and skips the rest?

omigtito profile image

  • Location Rio de Janeiro, Brazil
  • Work Frontend Developer at Pixter
  • Joined May 19, 2019

Thanks! Exactly what i was looking for!

nanacti profile image

  • Joined Apr 26, 2024

My solution

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink .

Hide child comments as well

For further actions, you may consider blocking this person and/or reporting abuse

itsrorymurphy profile image

Version Control: Git Basics

Rory Murphy - Apr 26

elizabethfuentes12 profile image

Add Generative AI to a JavaScript Web App 2.0

Elizabeth Fuentes L - May 9

karleeov profile image

How to Create a Monitoring Dashboard for Your Azure-hosted VM or Website

karleeov - Apr 26

toluxx profile image

A Deep Dive Into VUEX 4

Toluwanimi - Apr 26

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

TypeScript ESLint: Unsafe assignment of an any value [Fix]

avatar

Last updated: Feb 29, 2024 Reading time · 5 min

banner

# TypeScript ESLint: Unsafe assignment of an any value

The error "@typescript-eslint/no-unsafe-assignment: Unsafe assignment of an any value." occurs when you assign a value with a type of any to a variable or a property.

To solve the error, set the variable to a specific type or disable the ESLint rule.

Here are some examples of when the ESLint error is raised.

All of the assignments above cause the error because the ESLint rule prevents you from assigning a value with an any type to a variable.

The any type effectively turns off type checking and should be used sparingly.

This article addresses 2 similar ESLint errors:

  • @typescript-eslint/no-unsafe-assignment: Unsafe assignment of an any value.
  • Unexpected any. Specify a different type. eslint@typescript-eslint/no-explicit-any

# Disabling the @typescript-eslint/no-unsafe-assignment ESLint rule

One way to get around the ESLint error is to disable the rule.

For example, the following comment disables the rule for 1 line.

disabling the ts eslint no unsafe assignment rule

If you need to disable the @typescript-eslint/no-explicit-any rule for a single line, use the following comment.

If you need to disable multiple rules for a line, separate them by a comma.

If you need to disable the rule for the entire file, use the following comment.

If you need to disable the @typescript-eslint/no-explicit-any rule for the entire file, use the following comment instead.

You can disable both rules for the entire file by using the following comment.

If you want to disable the rules globally, add the following 2 rules to your .eslintrc.js file.

disable the two rules

If you use a .eslintrc.json file, make sure to double-quote the keys and values.

# Setting the variable or property to unknown instead of any

Alternatively, you can set the variable or property to unknown to resolve the ESLint error.

setting the variable property to unknown instead of any

The unknown type is the type-safe counterpart of any .

When working with the unknown type, we basically tell TypeScript that we're going to get this value, but we don't know its type.

We are going to check with a couple of if statements to track the type down and use it safely.

I have written a detailed guide on how to check the type of a variable in TypeScript .

When using the unknown type, you have to use an if statement as a type guard to check the type of the variable before you are able to use any type-specific methods (e.g. string, array, object, etc).

# The error commonly occurs when parsing a JSON string

The error commonly occurs when parsing a JSON string with the JSON.parse() method.

The result variable stores a value of any type because TypeScript doesn't know the type of the value that is being parsed.

One way to resolve the issue is to use a type predicate .

using type predicate to solve the error

The value is Employee syntax is called a type predicate.

Our function basically checks if the passed-in value is compatible with an object of type Employee .

Notice that in the if block in which we called the isAnEmployee() function, the parsed variable is typed as Employee and we can access the id and name properties without getting TypeScript or ESLint errors.

I've written a detailed guide on how to check if a value is an object .

# Resolve the issue by typing the variable explicitly

You can also resolve the issue by typing the variable explicitly and removing the any type.

Here is an example of typing an object.

And here is an example of typing an array of objects.

You might have to use a type assertion, e.g. when parsing a JSON string.

In some rare cases, you might have to widen the type to unknown before using a type assertion to set a more specific type.

I've written detailed guides on:

  • How to initialize a typed Empty Object in TypeScript
  • Declare an Empty Array for a typed Variable in TypeScript
  • How to add Elements to an Array in TypeScript
  • Check if an Array contains a Value in TypeScript
  • Check if a Value is an Array (of type) in TypeScript
  • How to declare an Array of Objects in TypeScript
  • How to declare a Two-dimensional Array in TypeScript
  • Declare Array of Numbers, Strings or Booleans in TypeScript
  • Create an Object based on an Interface in TypeScript
  • Create a Type from an object's Keys or Values in TypeScript
  • ESLint: Expected property shorthand object-shorthand [Fixed]
  • 'X' should be listed in the project's dependencies, not devDependencies
  • ESLint: Unexpected lexical declaration in case block [Fixed]
  • ESLint couldn't find the config 'prettier' to extend from
  • Import in body of module reorder to top eslint import/first
  • ESLint: A form label must be associated with a control

book cover

Borislav Hadzhiev

Web Developer

buy me a coffee

Copyright © 2024 Borislav Hadzhiev

  • DSA with JS - Self Paced
  • JS Tutorial
  • JS Exercise
  • JS Interview Questions
  • JS Operator
  • JS Projects
  • JS Examples
  • JS Free JS Course
  • JS A to Z Guide
  • JS Formatter
  • How to check interface type in TypeScript ?
  • How to Check the Type of an Object in Typescript ?
  • How to use Conditional Types in TypeScript ?
  • How to use Type Guards in TypeScript ?
  • How to declare nullable type in TypeScript ?
  • How to create conditional types in TypeScript ?
  • How to Get a Variable Type in TypeScript ?
  • How to express a Date Type in TypeScript ?
  • How to Type an Async Function in TypeScript ?
  • TypeScript Conditional Types
  • TypeScript Object Types
  • How to Return a Union Type in TypeScript ?
  • TypeScript Other Types to Know About
  • How to use generics in TypeScript?
  • Data types in TypeScript
  • How to Exclude Property from Type in TypeScript ?
  • How to install TypeScript ?
  • TypeScript in operator narrowing Type
  • TypeScript Generic Object Types

How to Check Types in Typescript ?

To write dependable, error-free TypeScript code, from type declarations to type guards and type checks, one must comprehend these techniques.

There are several approaches available to check the types in typescript which are as follows:

Table of Content

  • Using the typeOf Operator

Using the instanceof Operator

Using type guards, using the typeof operator.

The typeof operator is a straightforward way to determine the data type of a value. It returns a string representing the type, such as “string,” “number,” or “boolean.”

Example: In this code snippet, we demonstrate how to check the types of variables num and str using the typeof operator in TypeScript.

The instanceof operator in TypeScript verifies if an object inherits from a specific class, allowing you to check object lineage. This is valuable for working with custom classes.

Example: In this example, we define a Person class with name and age properties. We create an instance of Person called person1 and use the instanceof operator to check if person1 is an instance of the Person class. If it is, it logs a message confirming it.

Type guards are excellent characteristics that help with restricting a variable’s type according to specific conditions. They improve code readability and enable complex type checks.

Example: In this example, we define a type guard function isString that checks if the provided value is a string. We then use this function to conditionally check if unknownValue is a string, and if it is, we safely utilize string methods like toUpperCase().

Please Login to comment...

Similar reads.

  • Web Technologies

Improve your Coding Skills with Practice

 alt=

What kind of Experience do you want to share?

Variable Declaration

let and const are two relatively new concepts for variable declarations in JavaScript. As we mentioned earlier , let is similar to var in some respects, but allows users to avoid some of the common “gotchas” that users run into in JavaScript.

const is an augmentation of let in that it prevents re-assignment to a variable.

With TypeScript being an extension of JavaScript, the language naturally supports let and const . Here we’ll elaborate more on these new declarations and why they’re preferable to var .

If you’ve used JavaScript offhandedly, the next section might be a good way to refresh your memory. If you’re intimately familiar with all the quirks of var declarations in JavaScript, you might find it easier to skip ahead.

var declarations

Declaring a variable in JavaScript has always traditionally been done with the var keyword.

As you might’ve figured out, we just declared a variable named a with the value 10 .

We can also declare a variable inside of a function:

and we can also access those same variables within other functions:

In this above example, g captured the variable a declared in f . At any point that g gets called, the value of a will be tied to the value of a in f . Even if g is called once f is done running, it will be able to access and modify a .

Scoping rules

var declarations have some odd scoping rules for those used to other languages. Take the following example:

Some readers might do a double-take at this example. The variable x was declared within the if block , and yet we were able to access it from outside that block. That’s because var declarations are accessible anywhere within their containing function, module, namespace, or global scope - all which we’ll go over later on - regardless of the containing block. Some people call this var -scoping or function-scoping . Parameters are also function scoped.

These scoping rules can cause several types of mistakes. One problem they exacerbate is the fact that it is not an error to declare the same variable multiple times:

Maybe it was easy to spot out for some experienced JavaScript developers, but the inner for -loop will accidentally overwrite the variable i because i refers to the same function-scoped variable. As experienced developers know by now, similar sorts of bugs slip through code reviews and can be an endless source of frustration.

Variable capturing quirks

Take a quick second to guess what the output of the following snippet is:

For those unfamiliar, setTimeout will try to execute a function after a certain number of milliseconds (though waiting for anything else to stop running).

Ready? Take a look:

Many JavaScript developers are intimately familiar with this behavior, but if you’re surprised, you’re certainly not alone. Most people expect the output to be

Remember what we mentioned earlier about variable capturing? Every function expression we pass to setTimeout actually refers to the same i from the same scope.

Let’s take a minute to consider what that means. setTimeout will run a function after some number of milliseconds, but only after the for loop has stopped executing; By the time the for loop has stopped executing, the value of i is 10 . So each time the given function gets called, it will print out 10 !

A common work around is to use an IIFE - an Immediately Invoked Function Expression - to capture i at each iteration:

This odd-looking pattern is actually pretty common. The i in the parameter list actually shadows the i declared in the for loop, but since we named them the same, we didn’t have to modify the loop body too much.

let declarations

By now you’ve figured out that var has some problems, which is precisely why let statements were introduced. Apart from the keyword used, let statements are written the same way var statements are.

The key difference is not in the syntax, but in the semantics, which we’ll now dive into.

Block-scoping

When a variable is declared using let , it uses what some call lexical-scoping or block-scoping . Unlike variables declared with var whose scopes leak out to their containing function, block-scoped variables are not visible outside of their nearest containing block or for -loop.

Here, we have two local variables a and b . a ’s scope is limited to the body of f while b ’s scope is limited to the containing if statement’s block.

Variables declared in a catch clause also have similar scoping rules.

Another property of block-scoped variables is that they can’t be read or written to before they’re actually declared. While these variables are “present” throughout their scope, all points up until their declaration are part of their temporal dead zone . This is just a sophisticated way of saying you can’t access them before the let statement, and luckily TypeScript will let you know that.

Something to note is that you can still capture a block-scoped variable before it’s declared. The only catch is that it’s illegal to call that function before the declaration. If targeting ES2015, a modern runtime will throw an error; however, right now TypeScript is permissive and won’t report this as an error.

For more information on temporal dead zones, see relevant content on the Mozilla Developer Network .

Re-declarations and Shadowing

With var declarations, we mentioned that it didn’t matter how many times you declared your variables; you just got one.

In the above example, all declarations of x actually refer to the same x , and this is perfectly valid. This often ends up being a source of bugs. Thankfully, let declarations are not as forgiving.

The variables don’t necessarily need to both be block-scoped for TypeScript to tell us that there’s a problem.

That’s not to say that a block-scoped variable can never be declared with a function-scoped variable. The block-scoped variable just needs to be declared within a distinctly different block.

The act of introducing a new name in a more nested scope is called shadowing . It is a bit of a double-edged sword in that it can introduce certain bugs on its own in the event of accidental shadowing, while also preventing certain bugs. For instance, imagine we had written our earlier sumMatrix function using let variables.

This version of the loop will actually perform the summation correctly because the inner loop’s i shadows i from the outer loop.

Shadowing should usually be avoided in the interest of writing clearer code. While there are some scenarios where it may be fitting to take advantage of it, you should use your best judgement.

Block-scoped variable capturing

When we first touched on the idea of variable capturing with var declaration, we briefly went into how variables act once captured. To give a better intuition of this, each time a scope is run, it creates an “environment” of variables. That environment and its captured variables can exist even after everything within its scope has finished executing.

Because we’ve captured city from within its environment, we’re still able to access it despite the fact that the if block finished executing.

Recall that with our earlier setTimeout example, we ended up needing to use an IIFE to capture the state of a variable for every iteration of the for loop. In effect, what we were doing was creating a new variable environment for our captured variables. That was a bit of a pain, but luckily, you’ll never have to do that again in TypeScript.

let declarations have drastically different behavior when declared as part of a loop. Rather than just introducing a new environment to the loop itself, these declarations sort of create a new scope per iteration . Since this is what we were doing anyway with our IIFE, we can change our old setTimeout example to just use a let declaration.

and as expected, this will print out

const declarations

const declarations are another way of declaring variables.

They are like let declarations but, as their name implies, their value cannot be changed once they are bound. In other words, they have the same scoping rules as let , but you can’t re-assign to them.

This should not be confused with the idea that the values they refer to are immutable .

Unless you take specific measures to avoid it, the internal state of a const variable is still modifiable. Fortunately, TypeScript allows you to specify that members of an object are readonly . The chapter on Interfaces has the details.

let vs. const

Given that we have two types of declarations with similar scoping semantics, it’s natural to find ourselves asking which one to use. Like most broad questions, the answer is: it depends.

Applying the principle of least privilege , all declarations other than those you plan to modify should use const . The rationale is that if a variable didn’t need to get written to, others working on the same codebase shouldn’t automatically be able to write to the object, and will need to consider whether they really need to reassign to the variable. Using const also makes code more predictable when reasoning about flow of data.

Use your best judgement, and if applicable, consult the matter with the rest of your team.

The majority of this handbook uses let declarations.

Destructuring

Another ECMAScript 2015 feature that TypeScript has is destructuring. For a complete reference, see the article on the Mozilla Developer Network . In this section, we’ll give a short overview.

Array destructuring

The simplest form of destructuring is array destructuring assignment:

This creates two new variables named first and second . This is equivalent to using indexing, but is much more convenient:

Destructuring works with already-declared variables as well:

And with parameters to a function:

You can create a variable for the remaining items in a list using the syntax ... :

Of course, since this is JavaScript, you can just ignore trailing elements you don’t care about:

Or other elements:

Tuple destructuring

Tuples may be destructured like arrays; the destructuring variables get the types of the corresponding tuple elements:

It’s an error to destructure a tuple beyond the range of its elements:

As with arrays, you can destructure the rest of the tuple with ... , to get a shorter tuple:

Or ignore trailing elements, or other elements:

Object destructuring

You can also destructure objects:

This creates new variables a and b from o.a and o.b . Notice that you can skip c if you don’t need it.

Like array destructuring, you can have assignment without declaration:

Notice that we had to surround this statement with parentheses. JavaScript normally parses a { as the start of block.

You can create a variable for the remaining items in an object using the syntax ... :

Property renaming

You can also give different names to properties:

Here the syntax starts to get confusing. You can read a: newName1 as ” a as newName1 ”. The direction is left-to-right, as if you had written:

Confusingly, the colon here does not indicate the type. The type, if you specify it, still needs to be written after the entire destructuring:

Default values

Default values let you specify a default value in case a property is undefined:

In this example the b? indicates that b is optional, so it may be undefined . keepWholeObject now has a variable for wholeObject as well as the properties a and b , even if b is undefined.

Function declarations

Destructuring also works in function declarations. For simple cases this is straightforward:

But specifying defaults is more common for parameters, and getting defaults right with destructuring can be tricky. First of all, you need to remember to put the pattern before the default value.

The snippet above is an example of type inference, explained earlier in the handbook.

Then, you need to remember to give a default for optional properties on the destructured property instead of the main initializer. Remember that C was defined with b optional:

Use destructuring with care. As the previous example demonstrates, anything but the simplest destructuring expression is confusing. This is especially true with deeply nested destructuring, which gets really hard to understand even without piling on renaming, default values, and type annotations. Try to keep destructuring expressions small and simple. You can always write the assignments that destructuring would generate yourself.

The spread operator is the opposite of destructuring. It allows you to spread an array into another array, or an object into another object. For example:

This gives bothPlus the value [0, 1, 2, 3, 4, 5] . Spreading creates a shallow copy of first and second . They are not changed by the spread.

You can also spread objects:

Now search is { food: "rich", price: "$$", ambiance: "noisy" } . Object spreading is more complex than array spreading. Like array spreading, it proceeds from left-to-right, but the result is still an object. This means that properties that come later in the spread object overwrite properties that come earlier. So if we modify the previous example to spread at the end:

Then the food property in defaults overwrites food: "rich" , which is not what we want in this case.

Object spread also has a couple of other surprising limits. First, it only includes an objects’ own, enumerable properties . Basically, that means you lose methods when you spread instances of an object:

Second, the TypeScript compiler doesn’t allow spreads of type parameters from generic functions. That feature is expected in future versions of the language.

using declarations

using declarations are an upcoming feature for JavaScript that are part of the Stage 3 Explicit Resource Management proposal. A using declaration is much like a const declaration, except that it couples the lifetime of the value bound to the declaration with the scope of the variable.

When control exits the block containing a using declaration, the [Symbol.dispose]() method of the declared value is executed, which allows that value to perform cleanup:

At runtime, this has an effect roughly equivalent to the following:

using declarations are extremely useful for avoiding memory leaks when working with JavaScript objects that hold on to native references like file handles

or scoped operations like tracing

Unlike var , let , and const , using declarations do not support destructuring.

null and undefined

It’s important to note that the value can be null or undefined , in which case nothing is disposed at the end of the block:

which is roughly equivalent to:

This allows you to conditionally acquire resources when declaring a using declaration without the need for complex branching or repetition.

Defining a disposable resource

You can indicate the classes or objects you produce are disposable by implementing the Disposable interface:

await using declarations

Some resources or operations may have cleanup that needs to be performed asynchronously. To accommodate this, the Explicit Resource Management proposal also introduces the await using declaration:

An await using declaration invokes, and awaits , its value’s [Symbol.asyncDispose]() method as control leaves the containing block. This allows for asynchronous cleanup, such as a database transaction performing a rollback or commit, or a file stream flushing any pending writes to storage before it is closed.

As with await , await using can only be used in an async function or method, or at the top level of a module.

Defining an asynchronously disposable resource

Just as using relies on objects that are Disposable , an await using relies on objects that are AsyncDisposable :

await using vs await

The await keyword that is part of the await using declaration only indicates that the disposal of the resource is await -ed. It does not await the value itself:

await using and return

It’s important to note that there is a small caveat with this behavior if you are using an await using declaration in an async function that returns a Promise without first await -ing it:

Because the returned promise isn’t await -ed, it’s possible that the JavaScript runtime may report an unhandled rejection since execution pauses while await -ing the asynchronous disposal of x , without having subscribed to the returned promise. This is not a problem that is unique to await using , however, as this can also occur in an async function that uses try..finally :

To avoid this situation, it is recommended that you await your return value if it may be a Promise :

using and await using in for and for..of statements

Both using and await using can be used in a for statement:

In this case, the lifetime of x is scoped to the entire for statement and is only disposed when control leaves the loop due to break , return , throw , or when the loop condition is false.

In addition to for statements, both declarations can also be used in for..of statements:

Here, x is disposed at the end of each iteration of the loop , and is then reinitialized with the next value. This is especially useful when consuming resources produced one at a time by a generator.

using and await using in older runtimes

using and await using declarations can be used when targeting older ECMAScript editions as long as you are using a compatible polyfill for Symbol.dispose / Symbol.asyncDispose , such as the one provided by default in recent editions of NodeJS.

Nightly Builds

How to use a nightly build of TypeScript

The TypeScript docs are an open source project. Help us improve these pages by sending a Pull Request ❤

Daniel Rosenwasser  (58)

Last updated: May 16, 2024  

COMMENTS

  1. typechecking

    4.19.4 The instanceof operator. The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, and the right operand to be of type Any or a subtype of the 'Function' interface type. The result is always of the Boolean primitive type. So you could use. mySprite instanceof Sprite; Note that this operator is also in ActionScript but it shouldn't be ...

  2. Variable 'X' is used before being assigned in TypeScript

    Which approach you pick will depend on your use case. I prefer using initial values of the expected type if possible. # Additional Resources. You can learn more about the related topics by checking out the following tutorials: Argument of type not assignable to parameter type 'never' Type 'string or undefined' is not assignable to type string

  3. How to Check the Type of a Variable in TypeScript

    The function takes a parameter of type Person or Animal, so before we access a class-specific method, we have to check an instance of which class was passed to the function. # Using a type predicate to check the type of a variable. You can also use a type predicate to check the type of a variable.

  4. Methods for TypeScript runtime type checking

    It is possible to use io-ts to provide runtime type checks that are very similar in appearance to those produced by ts-runtime, and the library actually enables TypeScript to infer the relevant static types on its own. import x from "io-ts"; const CarType = x.type({. model: x.string, company: x.string,

  5. Type Checking In TypeScript: A Beginners Guide

    The most effective way to make use of runtime type checking is to create a multi-staged process that takes some data at runtime and assigns it to a known static type. The process is broken into 3 steps: Receive some data at runtime. Perform a runtime type check to assign it to a single static type.

  6. How to use type guards in TypeScript

    number. For anything outside of this list, the typeof type guard simply returns object. The typeof type guard can be written in the following two ways: typeof v !== "typename" #or. typeof v === "typename". typename can be a string, number, symbol, or boolean.

  7. TypeScript: Documentation

    Object literals are open-ended. In a .ts file, an object literal that initializes a variable declaration gives its type to the declaration. No new members can be added that were not specified in the original literal. This rule is relaxed in a .js file; object literals have an open-ended type (an index signature) that allows adding and looking up properties that were not defined originally.

  8. TypeScript: Mastering Type Assignment and Type Inference

    Type assignment in TypeScript is straightforward: it allows developers to explicitly specify the type of a variable, function parameter, or return value. This explicit typing helps catch type-related errors during compilation, long before the code is executed. Syntax and Usage. Type assignment is done using a colon (:) followed by the type.

  9. Type Checking In Typescript

    Type checking in Typescript on the surface seemed easy, but I went down a bit of a rabbit hole through documentation and guides that weren't always clear. So I want to try and simplify it down all right here, right now. Using The TypeOf Operator. Javascript actually has a typeof operator itself that can tell you which type a variable is.

  10. Improving TypeScript error handling with exhaustive type checking

    We'll use a TypeScript implementation of Rust's Result and Option objects, ts-results: There are other packages for TypeScript with very similar APIs, like NeverThrow, so you should feel free to play around. import { Ok, Err, Result } from "ts-results"; type Errors = "CANT_FETCH_TEMPERATURE" | "CANT_FETCH_HUMIDITY"; const getWeather = async (.

  11. Checking the type of an object in Typescript: the type guards

    The type guard is definitively wrong, and yet, Typescript is OK. But at runtime, undefined appear. And you see the too classic cannot read property 'alpha' of undefined coming…

  12. Run a TypeScript type check in your pre-commit hook using lint-staged

    As a separate step, if you want to check for TypeScript type errors (note: this is different to syntax errors which ESLint picks up and fixes for you), you would typically run a command in the CLI such as npx tsc --noEmit to compile the TypeScript and highlight any type errors to address. Now, the best case scenario here is that you simply pop ...

  13. TypeScript: Documentation

    When the type on the left of the extends is assignable to the one on the right, then you'll get the type in the first branch (the "true" branch); otherwise you'll get the type in the latter branch (the "false" branch).. From the examples above, conditional types might not immediately seem useful - we can tell ourselves whether or not Dog extends Animal and pick number or string!

  14. Why doesn't TypeScript catch that a const is being used before assignment?

    Since value is a const, it seems to me TS should be able to determine exactly when it's set and predict that this code crashes. console.log(getValue()); const value = "some string"; function getValue() {. return value; } In a second example without a function call, TS does catch that the variable is used before assignment: console.log(value);

  15. TypeScript ESLint: Unsafe assignment of an any value [Fix]

    Our function basically checks if the passed-in value is compatible with an object of type Employee.. Notice that in the if block in which we called the isAnEmployee() function, the parsed variable is typed as Employee and we can access the id and name properties without getting TypeScript or ESLint errors.. I've written a detailed guide on how to check if a value is an object.

  16. TypeScript: Documentation

    TypeScript has two special types, null and undefined, that have the values null and undefined respectively. We mentioned these briefly in the Basic Types section. By default, the type checker considers null and undefined assignable to anything. Effectively, null and undefined are valid values of every type.

  17. How to check the object type on runtime in TypeScript?

    1. No, You cannot reference a type in runtime, but yes you can convert an object to a type with typeof, and do validation/sanitisation/checks against this object in runtime. someKey: "string", someKey2: 1, type TypeWithAllOptionalFields = Partial<typeof plainObject>; //do further utility typings as you please, Partial being one of examples.

  18. How to Check Types in Typescript

    num is a number str is a string Using the instanceof Operator. The instanceof operator in TypeScript verifies if an object inherits from a specific class, allowing you to check object lineage. This is valuable for working with custom classes. Example: In this example, we define a Person class with name and age properties. We create an instance of Person called person1 and use the instanceof ...

  19. TypeScript: Documentation

    In a .js file, the compiler infers properties from property assignments inside the class body. The type of a property is the type given in the constructor, unless it's not defined there, or the type in the constructor is undefined or null. In that case, the type is the union of the types of all the right-hand values in these assignments.

  20. TypeScript: Documentation

    var declarations. Declaring a variable in JavaScript has always traditionally been done with the var keyword. var a = 10; As you might've figured out, we just declared a variable named a with the value 10. We can also declare a variable inside of a function: function f() {.

  21. Specify type of variable after initial assignment in TypeScript

    Specify type of variable after initial assignment in TypeScript. Ask Question Asked 7 years ago. ... Is there any way to accomplish this without TypeScript complaining about the type not containing the right properties? ... This essentially let the generic function check the type and return an object of type any.

  22. vuejs3

    code. The item of the v-for loop and the data type received by the knowledge.vue component are both KnowLedgeRes, why is the type check considered inconsistent? The assignment to knowledgeList is of the correct type. I tried vite to create a new vue3 project, and then perform similar operations.