TypeScript Crash Course •WIP•

The TypeScript Hierarchy

1/ Everything is a Type

The foundation of TypeScript is that everything — absolutely every variable, constant, or object — is a type.

You will always be declaring types, and chasing down the type declaration of your variables, constants, and objects to understand how to work with them.

Declaring your types means you are telling the TypeScript compiler “I know what type of thing this variable is.” Unlike duck-typed and loosely-typed languages, a strongly typed language like TypeScript means you should always have confidence that a variable is what it says it is.

Indeed, In the case when you don’t know what an input might be, TypeScript has mechanisms for that as well. Except in the case where you are receiving data from external inputs, you can be assured that the TypeScript compiler means this is true (More on that in section 4).

Variable, Constant, and Object Declaration

A type declaration is really easy to spot: It is just like the Javascript you already know (var, let, const), but has two extra elements: a colon (:) and a type declaration.

TypeScript type declaration example

When you first declare a variable, constant, or object within the current scope (remember, it’s just Javascript so all of the scoping rules of Javascript you know also apply), you will probably declare its type.

var foo : string; 

Create variable foo (in local-scope) and tell the TypeScript compiler it is a String. The only exception is when assigning a variable to a literal, TypeScript can infer the type declaration for you.

foo = "Hello world";

Here, TypeScript already knows that "Hello world" is a string (it knows this because it is surrounded by quotation marks. We call this a string literal.) Likewise, since we are assigning the string literal directly onto the variable foo when we declare it. In this case, we can leave the type definition off completely and just rely on “good old Javascript” syntax.

This assumes you have the noImplicitAny setting set to true (see below), which is strongly recommended.

Function Parameter Declaration

All functions which take inputs in TypeScript (arity) have declared types. Hence, that means that when you declare a function you will also be declaring types for the inputs (variables) passed to the function. This is referred to as “function type declaration” and looks like this:

Return Value Declaration

All functions which return a value (if they return a value), that has a declared type return value. Indeed, this means that when you are working with TypeScript, the compiler is checking your type declarations. How does it do this?

Well, it does this using a Typescript compiler, which we will configure in the next section. It can do this because of two things: (1) An integration with your IDE (VSCode, WebStorm, or Rubymine) or powerful shell editor like vim means that when the coding tool itself runs, and (2) the settings specified in your Typescript config file tsconfig.json, which lives at the root of your project and specifies important things like what the target Javascript version is and what rules TypeScript should be used for your project.

The code you write is always being checked in the background for TypeScript problems, which are nearly always type declaration mismatches.

2/ Getting TypeScript

To install tsc for the first time (the command line tool used to work with TypeScript), you must first install it using Yarn or NPM.

If you use yarn, run:

yarn global add tsc

If you use npm, run:

npm install -g tsc

Alternatively, you can also install it globally using Homebrew, like so:

brew install typescript

Check to make sure you have the tsc command install using which tsc

which tsc
use which tsc to find if you have the tsc command installed

If you see the path to the tsc command, you are OK to continue. If you see “tsc not found” you do not have the command correctly installed.

4/ TypeScript Config

Let’s make our first TypeScript experiment and learn how to set up our TypeScript config. The Typescript config is the all-important configuration file that tells TypeScript exactly what to do. Since TypeScript has several different options, this file is very important to understand. In this crash course, we will cover only the first few and most important options for setting up new projects.

Create a new empty project using mkdir HelloTS

Then cd HelloTS to change directory into that directory.

Then use tsc --noImplicitAny --init to create a new basic .tsconfig file:

tsc --noImplicitAny --init explanation of how to set up a new TypeScript project

Let’s take a look at our new file in our code editor:

tsconfig default file
The default TypeScript config file when using --noImplicitAny

Notice that nearly every option is commented out (shown as light grey), which means it doesn’t apply.

Target

The first option not commented out is the target, which specifies which version of Javascript TypeScript will compile to. For compatibility with the oldest browsers, you’ll need to use ES5. But if you want compatibility with only new browsers, you can set this to a newer version of JavaScript (or ECMAScript, JavaScript’s technical name). Here by default, we are compiling to ES5.

Module Style

Next is the module setting, which specifies which style of module declaration to use: AMD or commonjs.

Strict

Next is the strict mode setting, which we have set here to true. By default, if you have strict mode enabled, all of the “strict” settings will also be enabled (even if they are commented out in the .tsconfig file):

  • noImplicitAny
  • strictNullChecks
  • strictFunctionTypes
  • strictBindCallApply
  • strictPropertyInitialization
  • noImplicitThis
  • alwaysStrict

noImplicitAny

We haven’t actually learned what the “any” type is at all (more on that in Lesson 6), but this tells TypeScript that all variables, constants, and objects must have known Types (that is, we must declare them or let TypeScript infer them when we write our code.) This is the fundamental paradigm of TypeScript itself and how we introduced TypeScript above, so that’s why it is presented here as the default setting.

strictNullChecks

strictFunctionTypes

strictBindCallApply

strictPropertyInitialization

noImplicitThis

alwaysStrict

Should I use strict mode in TypeScript or not?

If you just start a new project using strict mode in TypeScript is strongly recommended. It will help you to avoid errors, typos, and mistakes in your code.

Strict mode constricts you in how you can write your code.

If you want to have just some of the strict mode settings, you can opt-in parameter by parameter. (For example, if you have an existing codebase you want to convert over into TypeScript.)

You probably won’t be able to set parameter strict to true in an existing project. But you can set some strict mode parameters to gradually rewrite your codebase using strict mode rules.

For example, you can set the parameter noImplicitAny to true and rewrite your code to one that is following this rule (you should write a type of parameters in every function).

5/ Compiled vs. Executed

One of the key things to understand about TypeScript is that type checking happens when your code is compiled, not necessarily when it is executed.

The only time a static type is checked in TypeScript is during compilation.

The resulting JavaScript doesn’t know anything about the types you’ve created.

This means that you aren’t really guarding against the kind of invalid input you may encounter coming from an external source.

By default, TypeScript does not verify types at runtime. This avoids runtime overhead and also aggressively optimizes runtime performance.

Contextual information about types is removed during compilation because we are compiling to pure JavaScript. Because the type checking is not as strict in JavaScript as it is in other languages, JavaScript doesn’t know the types at runtime.

So how do you enforce a thing is the correct type at runtime?

  • TypeScript type guards: Type guards restrict the scope of variable types through conditional blocks and determine the type of variable to expect during code execution
  • Validation libraries: These libraries provide ready-made boilerplate code, methods, and interfaces to perform schema validation for your variable types
  • JSON schemas: The popular data interchange format for fast, easy, lightweight, and quick data transfers as well as conversion for the different entities to communicate.
  • Manual checks: Manually maintaining the set of rules and applying it on a specific language to validate the types of data values — this is a cumbersome way of single-handedly managing rules with no other runtime type checking approach or library used, and eventually creates an unstable system.

EXAMPLE

6/ any: The Godfather Type

7/ unknown: The Mystery Man Type

8/ null, undefined, never

9/ Basic Types

10/ Symbols & Objects

11/ Aliases, Unions, Intersections

12/ Array Type

13/ Tuple type

14/ Enums

15/ Classes & Interfaces

16/ Functions and Return Types

17/ Subtypes and Supertypes

18/ Type Widening

19/ Escape Hatches

Leave a Reply

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