What are TypeScript’s any, void, never, undefined and null types

Unlike intuitive types such as string or number, the types any, void, never, undefined and null might cause a confusion for a newbie TypeScript developer. In this post, I will share what I have learned about these types.


The simplest one is any. From the spec:

The Any type is used to represent any JavaScript value. The Any type is a supertype of all types and is assignable to and from all types. In general, in places where a type is not explicitly provided and TypeScript cannot infer one, the Any type is assumed.

So, the any is the way how so-called optional typing is implemented in TypeScript. With any you effectively switch off the type checking and working in “JavaScript mode”. It is very useful when you gradually migrate from JavaScript and have not yet figured out all types. This is one of the core TypeScript’s value propositions.

undefined, null and –strictNullChecks

From the spec:

The Null type corresponds to the similarly named JavaScript primitive type and is the type of the null literal. The Undefined type corresponds to the similarly named JavaScript primitive type and is the type of the undefined literal. The Null type is a subtype of all types, except the Undefined type. The undefined type is a subtype of all types.

So from the spec, these types are the types whose domains consist of only one value and undefined is a specialization or subtype of null. However, despite of having this specialization relationship you still can assign null to the variable of type undefined and vice versa, hence more specifically the domains of both null and undefined are two values null and undefined:

The compiler option --strictNullChecks enables strict null checking mode, which removes the null and undefined values from the domain of every type and is only assignable to themselves and any. I believe this is much simpler to understand and predict how TypeScript will behave in different situations. Strict null checks provide us with the tool to increase the code reliability and therefore I recommend to always have it switched on unless you are dealing with a huge legacy system which does not compile because of this compiler switch.


From the spec:

The Void type, referenced by the void keyword, represents the absence of a value and is used as the return type of functions with no return value. The only possible values for the Void type are null and undefined. The Void type is a subtype of the Any type and a supertype of the Null and Undefined types.

This seems to be the pretty simple type with the domain of two values null and undefined. But as we learned earlier, the same domain is defined for null and undefined types. While the need for later types is dictated by Javascript, why we need yet another type with the same domain? Well, first, with --strictNullChecks the domains become not the same and second, we want to logically differentiate between function which might return undefined in some cases and function which does not expect to return any value at all:


The never type represents the type of values that never occur. It is a bit weird, but it perfectly makes sense in cases when function never returns a value or when you write a code in the code branch which will never execute:


Updating an immutable object in TypeScript

Recently I could not figure out how to types-safely update immutable object in TypeScript. The problem is that in JavaScript the object spread feature is used for this task, like so:

But if you have accidentally missspelled the property you want to update, object spread will add a new propery instead of updating old one:

So I asked StackOverflow for help and got an excellent answer to use a helper function:

Now if I want to update my point but make a mistake – I get a compiler error:

All is great now, but someone on StackOverflow asked to explain each line of this update function. It is not simple indeed and uses latest TypeScript features. The author did not respond, so I will try to explain it here.

The most complex line is first one:

function update<T, K extends keyof T>(obj: T, updateSpec: Pick<T, K>): T {

This line incorporates two pretty complex typescript features: generics and indexed type query. I highly recommend clicking links provided in the previous sentence and reading articles provided by TypeScript official documentation, which is great. But here is my shallow explanation.

A generic type is the type which is not known when the function is defined but only becomes know when the function is used somewhere in the code (all is at development time, not runtime). For example in the given update function the type T is not known, when function is defined, but at this point o1 = update(o1, {y:1}); the type T becomes of type Point. So, for now, we know that in this particular call, function update will take a Point and will also return a Point.

Indexed type query gives you an ability to query at compile-time the names of the properties of some type. For example, type PointProperties = keyof Point; // "x" | "y". That is from the variable of type Point we can query the property named x or y or both. So the expression K extends keyof T means in our case that generic type K must be something compatible with "x" | "y":

Ok, now we know exactly the type constraints of update function, but what is Pick<T, K>? It is the type, added to TypeScript standard library in version 2.1 and is mapped type:

You can think of it as a type which consists of a subset of properties of type T, described by K. So when we have a point defined this way: interface Point { x: number, y: number }, the type Pick<Point,"x"> is basically {x:number} and Pick<Point,"x" | "y"> is { x: number, y: number }

Hence to conclude, the first line of the function just tells its name and that it takes as the first parameter the value of unconstrained type T, returned value is of the same type. And the second argument must be some type, which contains any subset of properties, defined in T. So, if you want to update the Point, the only eligible values for the second parameter of update are: { y: 1 }, { x: 1 }, {}, { y:1, x:1 } (note: number 1 is arbitrary here, any number will do; if compiler option --strictNullChecks is not on, then values undefined and null are also eligible).

All other lines are not nearly as interesting :-). Second line: const result = {} as T just creates creates an empty object and tells TypeScript to treat it as the value of type T. Third line Object.keys(obj).forEach(key => result[key] = obj[key]) is pure JavaScript which copies all properties from input object obj to the result object. Fourth line Object.keys(updateSpec).forEach((key: K) => result[key] = updateSpec[key]) does the same for updateSpec, this actually replaces old values of obj with new ones from updateSpec. And finally the function returns: return result;.


How TypeScript can teach you web standards

Recently my team decided to migrate all views in our Angular 1.x application to React. To me, the value of React is doubled if used together with TypeScript. I enjoy developing views when I have autocompleted and static checks for the view model, used in the view. I also can safely rename elements of the view model, which I prefer doing often and after I have already worked with the model and realized what the best name could be. Moreover, JSX in TypeScript is also statically checked, so you can not make mistake there as well. All these merits are impossible in Angular 1.x and Angular 2. Both have special DSLs for describing views and of course those DSL are not as powerful as TypeScript when it comes to static type checks.

But what I have discovered is that TypeScript can even teach you web standards. These standards are not very consistent but we still have to use them correctly. Let me give you few examples.

First, TypeScript can teach you JavaScript itself and save you from silly errors. Look at this code and try to answer the questions (--strictNullChecks):

After you gave it some thought, here is the answer: the type of y is never and type of z is number | null. If you answered correctly, you are great JavaScript developer with exceptional memory. The type never is given to y because that line will never execute. This is because of JavaScript’s design flaw which defines this expression to be true: typeof null === 'object'. Having learned this, now, what are the types of y and z in the following snippet?

Well, you are totally right, y is null, because 'typeof null === 'object' is true. And since true part of the if statement has dealt with null, the type of z is just a number.

Now, let’s have a look at a second example, now TypeScript will tech us something about DOM API. Have a look at the following code, which does not compile (it used to handle React button click event):

What??? This way official React’s page teaches us to access values from the event targets!

React docs snapshot

When we first encountered this problem, we decided there was a bug in TypeScript type definitions for React (those are installed with npm install --save-dev @types/react). But later we discovered, those type definitions actually have another property for SyntheticEvent<T> (type of target), named currentTarget, whose type is different:

Hmmm, let’s see what dumentation has to say about currentTarget:

Identifies the current target for the event, as the event traverses the DOM. It always refers to the element to which the event handler has been attached, as opposed to event.target which identifies the element on which the event occurred.

While target is

A reference to the object that dispatched the event. It is different from event.currentTarget when the event handler is called during the bubbling or capturing phase of the event.

So are we interested in the value of the element where we placed an event handler or in the element, which dispatched the event? target is the origin of the event, which no one really cares about, it might be a span inside a link, for example, while currentTarget is something you should very much care about, as this is the element, to which you have attached your event handler. See more discussion on this topic here . We have learned this only because TypeScript caused us to dig deeper into targets and see the difference. Without it, we would have used target and wait for problems to come (someone might place child element to the element we were listening to and target would become this child element!). So, thanks, TypeScript and those great people who write type definitions for lessons :-).


Вступ до програмування на TypeScript

Дебютував у царині проведення вебінарів :-). Трохи виснажливо, трохи незвично говорити російською, трохи переслідував мене demo ефект, коли щось ломається або не працює у самий непідходящий момент, але я ніби впорався. Принаймні покищо лайків більше ніж дислайків :-).


My 2 cents on why you should try TypeScript

It has been said a lot about the value proposition of TypeScript. But I still want to add my 2 cents and talk about the reasons to use TypeScript from the personal perspective. So, here is my list:

  • It’s main designer is Anders Hejlsberg. Well, strange reason, I know. But this guy has designed Turbo Pascal, Delphi, and C#. All widely adopted and great technologies. People love them. Let’s face it, experience matters and I think the guy with such a background behind TypeScript increases its chances to succeed.

  • It is supported and developed by Microsoft. Again strange reason, but it seems to me companies are good at certain things and it is very hard for them to become good at other things. Look at to Google’s social network Google+. Is it nearly as good as Facebook? Another example is Microsoft’s Bing search engine. Is it as good as Google’s search? Microsoft has always been good at creating great developer tools and this increases TypeScript’s chances to succeed.

  • Scientific research shows that static typing can help developers with maintenance tasks. This is a more objective reason and it is the core one. We have hard data which proves that TypeScript improves your effectiveness at JavaScript maintenance tasks. And you don’t have to reflect hard to see the reason. I have parts of my project at work in TypeScript and after working in such a part for some time I get used to autocompletion, “Find definition/pick definition” and renames. Recently I had to fix a pretty complex bug in the pure JavaScript codebase and I struggled a lot. Not that it was very hard to understand the code, but it was extremely hard to navigate in the code. For example, I open the function. I see it calls another function with some parameter. I want to see what this parameter is, like what is its type or whether it is an array or not but I can not. Now I want to see the definition of that called function but for this, I need to search the whole codebase. After I have found the definition I want to go back to the caller but this is again not a simple key stroke as it would be in TypeScript. I don’t even think about renaming the function. It simply does not work. To rename you have to go through all the codebase again and try to understand where you function is called and if this is your function or some other function with the same name. Entering new code in TypeScript is also faster than in JavaScript. This is because you just hit dot and you have the list of API options you can select while in JavaScript you have to remember all the API details. Since remembering never works, at least in my case, you have to search the web for API documentation to recall or learn how to use it. Hence TypeScript improves API discoverability. Pathikrit Bhowmick summarized experience with type system:

    Static typing is like spell check.
    Dynamic typing = writing your essay in Notepad.
    Static typing = writing your essay in Word (or Grammarly).

  • TypeScript is the mature and open source project. And this is important for production. It was introduced in 2012, so at this point, it is 4 years old language. It has many great and stable features, its compiler is fast and can be tuned to be even faster. It has native support in most popular IDEs like WebStorm and Visual Studio. It has a lot of tooling around it and even linter. It has huge, community developed code base of type definitions for popular JavaScript libraries and frameworks. It has a vibrant community which produces courses and answers questions on the forums. All in all, today starting doing a production with TypeScript is relatively easy and smooth.

  • TypeScript can help to deal with JavaScript’s quirks. It can not fix everything as it is a superset of JavaScript, but it can help a lot with some JS problems. For example, it can help you to not mess up your “this” accidentally. It will not allow you to forget to write your return statement and fall into the trap of semicolon auto insertion. It will not give you a chance to create a global variable inside the function. It will help to avoid shooting yourself in the foot with type coercion. I will write another blog post explaining how TypeScript fixes JavaScript later.

  • Fool protection. Here in Ukraine, we tell the system is fool protected if it has means to protect itself from user’s incompetent actions. For example, one can not insert battery the wrong way into the smartphone or insert the cable into the wrong computer’s port. This whole concept of inserting something only where it fits is extremely suitable to describe a type system. With TypeScript I can only insert a variable into the function where it fits. And this is great. Not that I am a total fool. I would like to be focused on solving the business problem not on whether it is ok or not passing something into the function. I also can be tired or ill or disappointed or distracted or not fluent with current codebase or whatever. All these reasons can cause me to make a fool mistake which will be corrected immediately by TypeScript and which would live until runtime in JavaScript.

  • Typescript is zero risk to your project. This is because it generates beautiful idiomatic JavaScript. Hence had you wanted to move back to JavaScript – you would just compile your TypeScript sources to JavaScript and off you go.

  • You can generate definitions of your RESTfull API from your backend code. This way if some breaking change happens on the back-end, after declarations regeneration, you will immediately get a compiler error and we all know, the faster feedback loop the better.

To summarize, I would say I just feel more relaxed doing TypeScript. It gives similar to C# and VisualStudio joy of coding.


Overcoming TypeScript’s type guards limitations in the nested scope

TypeScript has a cool feature – control flow analysis which allows to narrow down the variable’s type inside the control structure block. Like in this example:

Very different from C# or Java, right? It made me thrilled when I first learned about it. It has a limitation, though:

It is a bit puzzling at first glance. Why? How in the world x is not a number in the else block? The root cause here is that TypeScript doesn’t know what map function is going to do with the lambda function passed to it. If map will invoke the lambda right away – that’s fine, but what if it, for example, will call setTimeOut and pass the lambda function into it? In such a case i => i * x will be executed after the timeout, well after control flow will leave the else block. Because of this possibility TypeScript takes a pessimistic position and considers x to be number | string inside lambda function i => i * x.

How to deal with this problem? There are several approaches. First, you can tell TypeScript that nothing bad is going to happen by limiting the scope of x. For now, it is global. Limiting it to the function, which does not change x fixes the problem:

Second, introduce another variable of needed type inside else block. Since TypeScript now knows exactly the type of the variable, it will not complain:

Finally, let TypeScript know that you are not going to change x after it’s initialization and it will calm down:

I have introduced someFunciton only to stop type inference mechanism to infer the type of x from the assignment. If I wrote const x: number | string = 5, for example, the type inference mechanism would have inferred the type of x to be number (never inside if block) even though I have declared it to be number | string.