TypeScript: The Hard(er) Parts: Part 2

John Tucker
codeburst
Published in
5 min readDec 13, 2018

--

Wrapping-up an on-boarding guide to using TypeScript for JavaScript developers.

This is the second and last part of a series starting with, TypeScript: The Hard(er) Parts: Part 1.

Type Assertions

Sometimes you’ll end up in a situation where you’ll know more about a value than TypeScript does. Usually this will happen when you know the type of some entity could be more specific than its current type.

Type assertions are a way to tell the compiler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler. TypeScript assumes that you, the programmer, have performed any special checks that you need.

— TypeScript — Basic Types

There is one common situation where type assertions are naturally required; consuming JSON from an external API. The following example simulates this situation:

Observations:

  • In this case, the type assertion is in line 11; where obj (any type) is being asserted to be of type Hello
  • Notice that we perform a runtime check to validate obj before asserting the Hello type; this is important to prevent unexpected errors later on
  • Also, we implemented the check using a simple (but imperative) approach. In a typical and more complex example we will want to use a library like AJV to perform the checks in a more declarative manner
  • Finally, we could have implemented the same type assertion as follows; more of a stylistic choice:
...
const myHello = obj as Hello;
...

Generics

In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

— TypeScript — Generics

The first time one is likely to encounter generics is with promises; in this case we create our own promise that resolves to a string:

Observations:

  • Because promises can resolve to anything, we need to explicitly supply the string type to the generic Promise class; with this we can be assured that bar is typed as a string
  • When I first wrote this example, I was surprised that the string type could not be inferred; I am guessing that is asking for a lot from the TypeScript compiler
  • Also, when I started this article, I was going to use the Array generic as the first example; found that tslint complains if we do this
[tslint] Array type using 'Array<T>' is forbidden for simple types. Use 'T[]' instead. [array-type]

While it is more common to simply use generics (like Promise), there are times we will want to create our own generics.

In this artificial situation, let us imagine we want to create a function that given an argument (with type) it returns that same argument (with type); an identity function (preserving types).

Since JavaScript does not have types, this is trivial.

In TypeScript, however, we are forced to create multiple functions; one for each type, e.g. string and number.

Enter generics; we now can have a single generic identity function that works with all types:

If you haven’t already guessed, we can simplify this further as the TypeScript compiler will do type argument interferences:

Also, with the advent of arrow functions in ES2015, we tend to avoid using the function syntax unless we are defining methods. So we can use the following for generic arrow functions.

One tricky thing, however; if we need a generic arrow function in a file with JSX in it (with extension .tsx), we need to use a slightly different form to give a hint to the TypeScript compiler that we are defining a generic instead of a JSX element.

Intersection and Union Types

On occasion, one will find oneself with multiple interfaces, e.g., A and B, and then find the need for a third type that has all the members of both types. Would could do this succinctly using an intersection type (A & B):

note: (Don’t worry if the following does not make sense, just added to provide evidence that intersection types do come up.) A real-world (front-end) example of this when using React Redux and we have two interfaces, one related to each of mapStateToProps and mapDispatchToProps), and through the connect HOC, the connected component receives properties that comprise an intersection type of the two interfaces.

Similarly, one will find oneself with multiple interfaces, e.g., A and B and have the need for a type that is either one of them; here we can use the union type (A | B):

note: (Don’t worry if the following does not make sense, just added to provide evidence that union types do come up.) A real-world (front-end) example of this when using Redux; each action will have its own corresponding interface and reducers need to accept any (union type) of them as a parameter.

Type Aliases

One problem that we encounter when using intersection and union types is that we start repeating these types; the solution is to create an alias to this type, e.g., AOrB below:

Wrap Up

In looking through a number of my recent projects, these are about all the topics that a JavaScript developer will likely encounter when transitioning to TypeScript (with the assumption that one is taking a conservative path).

Of course, on the other hand one will likely find that with new-found TypeScript constructs, there will be projects (internal or third-party) that “go crazy” and start using them heavily, e.g., decorators in the Ts.ED (A TypeScript Framework on top of Express) library.

For now, my preference is to stick close to my JavaScript roots and incrementally explore and adopt new features when they make for more succinct and (importantly) declarative code.

BTW… I am going to take a look at decorators; they look interesting.

--

--