Introduction to Validating Code with Advanced TypeScript
Get an introduction to advanced Typescript.
We'll cover the following
Throughout this course, we’ve been using TypeScript to make assertions about the structure of our code to make it easier to validate that our code is correct. Now we’re going to focus on features of TypeScript itself that we can use to enhance the typing of our system.
Many JavaScript programs use a lot of code to protect against invalid data being passed around, continually performing null checks or other type checks. We can also use our TypeScript system to make certain kinds of invalid states impossible without using runtime checks.
We can, for example, specify that a certain value can’t be set to null, and then at compile-time, the compiler must be convinced that a null value can’t get there. Some of these techniques are more verbose than plain JavaScript, but the hope is that the extra typing upfront makes the runtime behavior of the code a lot easier to deal with over the long term.
In this chapter, we’re going to look at a few different helpful TypeScript features: union types, which allow us to create new types by combining existing types; literal types and enums, which let us limit a type to a set of values; and mapped types, which allow us to apply a feature to an existing type. Then we’ll explore configuring TypeScript to change the behavior of the compiler itself.
Creating union types
I want to start with a concept we’ve already seen, but which I want to put a little more depth behind because it winds up being important to a couple of other TypeScript techniques. The feature is called a union type, and we used it to create the type checking for our action types in our various reducers in the Managing State in Stimulus Code and Managing State in React chapter.
You can create a union type by combining existing types, both internal or of your own creation with the pipe operator ( |
). One of our union types, for example, looked like this:
type Action = AddFilterAction | ToggleDateAction | ClearAllAction
We’re using the type
keyword here to assign a type to a new name, which we then use in our method signature:
static dispatch(action: Action): CalendarFilterState {
We don’t have to use the type
keyword, though; we could use the union type inline:
static dispatch(
action: AddFilterAction |
ToggleDateAction |
ClearAllAction): CalendarFilterState {
We use the type
keyword for the same reasons we’d name any variable: to give a complex operation a simpler, more semantically meaningful name making the type easier to use and the code easier to read.
Now we can say that a variable is a member of a specific type, like let s: AddFilterAction
, or we can declare the variable to be a member of the union type, as in let u: Action
. These two declarations have different meanings and allow access to different attributes.
When we declare the variable as a member of the specific type, the let s: AddFilterAction
, then TypeScript lets the variable have access to all the attributes of AddFilterAction
.
When we say that a variable is a member of the union type, as in let u: Action
, that declaration in TypeScript only lets that variable have access to methods or attributes that are common to all the specific types that make up the union. In this case, we have three different types in that union. All three of them have a type
attribute, which means that we can call u.type
. As it happens, two of those types have a dateString
attribute, but the third doesn’t, so if we call g.dateString
, then TypeScript will fail to compile.
All that said, if you look in our reducer code, back in the Refactoring to the Reducer Pattern and Managing State and Front-End Development lesson, you’ll see that we do declare the action as the generic type Action
, and then inside the case statements, we do, in fact, call attributes like action.dateString
, so there must be some way to tell TypeScript that you are looking at only one of the specific types of the union type. TypeScript actually has a few different ways of differentiating between types. We’ll talk about the specific one used in our reducer code in the Using Enums and Literal Types lesson.
Get hands-on with 1200+ tech skills courses.