The introduction to Typescript you may be missing
Tags: typescript, javascript
Published: 2019-09-17 21:58:28

_Photo by Amar Yashlaha on Unsplash_

Last year I wrote a post like this about Angular, and I think it was really helpful for new corners. I want to do the same for new people reaching Typescript. 



## What is Typescript?

Typescript is an open-source programming language developed and maintained by Microsoft. It's a strict superset of JavaScript, adding optional static typing to it. It was first launched on October 1st, 2012, almost 7 years ago, and now is version 3.7, after many releases. Because Typescript doesn't follow the semantic version, each version introduces both new features and a few breaking changes. Until this year the release cycle was one new version every two months, but they have moved to a 3 months release cycle to improve the stability of each release. It is developed with TS and compiled to JS using the TSC, meaning it compiles itself.

In early versions, Typescript introduced concepts that were not stable into JS, such as classes, modules, and others. These concepts lead to the misbelief that Microsoft was trying to create a new scripting language to replace JavaScript, rather than a language to improve JavaScript itself. In newer versions, Typescript has adopted the new features of newer versions of the ECMAScript spec and has improved the integration with plain JavaScript files as well. 

## What means for Typescript to be a superset of JavaScript? 

In plain words, every JavaScript source file _should just work_. But, that's not always the case. In order for this to be true, you should disable Typescript `strict` type checking, that's enabled by default since its introduction in [version 2.3](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html#new---strict-master-option). But, then you won't be leveraging of the type checking as you should. If you try to compile any JS source file with the Typescript compiler just by changing the extension from `.js` to `.ts` you probably will find some complaints from the `tsc`. 

## Introducing Typescript Compiler (`tsc`) and type annotations

We are going to see an example. First, we are going to install `typescript`. You can install it globally, but for this example, I'm going to create an npm project. Make sure you are using the latest Node LTS.

1. `$ mkdir ts-example`

2. `$ npm init -y`

3. `$ npm i typescript`

4. `$ touch fibonacci.ts` 

5. We paste the code:

   ```ts
   function fibonacci(num, memo) {
     memo = memo || {};
   
     if (memo[num]) return memo[num];
     if (num <= 1) return 1;
   
     return (memo[num] = fibonacci(num - 1, memo) + fibonacci(num - 2, memo));
   }
   ```

6. `$ npx tsc`

7. See those errors in the console:

   ```shell
   fibonacci.ts:1:10 - error TS7023: 'fibonacci' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
   
              ~~~~~~~~~
   
   fibonacci.ts:1:20 - error TS7006: Parameter 'num' implicitly has an 'any' type.
   
   1 function fibonacci(num, memo) {
                        ~~~
   
   fibonacci.ts:1:25 - error TS7006: Parameter 'memo' implicitly has an 'any' type.
   
   1 function fibonacci(num, memo) {
                             ~~~~
   ```

8.  Or, if you are using an editor:

   ```ts
   function fibonacci(num, memo) {
            ~~~~~~1   ~~2  ~~3
     memo = memo || {};
   
     if (memo[num]) return memo[num];
     if (num <= 1) return 1;
   
     return (memo[num] = fibonacci(num - 1, memo) + fibonacci(num - 2, memo));
   }
   ```

This is a valid JS function. A Fibonacci function that storages the result of previous calls in memo `object` and then return again in a recursive calling, until it reaches the end.[1](1) Though Typescript reports 3 errors, all of them are likely the same, but we are going to check them individually:

1. `'fibonacci' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.`
2. `Parameter 'num' implicitly has an 'any' type.`
3. `Parameter 'memo' implicitly has an 'any' type.`

What does that mean? You may ask. Well, that it's Typescript telling you that it's unable to do what it supposed to do, type. It doesn't have information about what `fibonacci` return type is, neither what should the parameters be. But, we can fix that, because we know this information and we can provide TS with it.

1. `fibonacci` function should return a number.
2. `num` parameter should be a number.
3. `memo` parameter should be an object.

Now we use the Typescript type annotations to provide that information that to the `tsc`. Type annotations in Typescript are descriptions that start after the declaration of the variable with a colon followed by a type. For functions and methods, that description is for the return type rather than for the function itself.


```ts
  var a: number;
  // "var a" is the regular JS variable declaration, and ": number" is the type annotation.
  function a(): string {/* */}
  // "function a() {}" is the regular JS function declaration, and ": string" is the return type annotation.
```


So, we use this for our Fibonacci example and run the `tsc` command again, or if we are in an editor:


```ts
function fibonacci(num: number, memo: object): number {
  memo = memo || {};

  if (memo[num]) return memo[num];
      ~~~1              ~~~2
  if (num <= 1) return 1;

  return (memo[num] = fibonacci(num - 1, memo) + fibonacci(num - 2, memo));
          ~~~3
}
```

Now we have a second error. And this is often the point when people start complaining.

-  _You know what? F*ck it! It's not my fault Typescript doesn't understand this simple code_. Well... Maybe, but do you understand better the function with or without the annotations? With the type annotation, you can read directly the type you need, and how to use it.  
- _I can annotate my code with JSDocs_. Yes, you could, but there is nothing that would check your code, but maybe the Typescript compiler itself, more on that later, and if you already going to use the `tsc` why don't write `ts` files?

## Introducing interfaces, types, and type aliases

Back the errors, we can notice that it's actually the same error: `Element implicitly has an 'any' type because the expression of type 'number' can't be used to index type '{}'. No index signature with a parameter of type 'number' was found on type '{}'.` That's maybe not the best description of the problem but is something you may read a lot. `tsc` is complaining about `memo` having the `object` type. In `strict mode`, `object` type is equal to an empty object (`{}`), and accessing a property that is not declared in the variable type would mark that property as any, because Typescript doesn't know it's typing, but it could assume it does exist somehow, but not in `strict mode`. Since we are in `strict mode` Typescript is telling us: _Hey, you are trying to access a property that I don't know of, and I could give it to you like any, but you don't want that either. So, I need you to give the right typing information about this property you are trying to get_. 

We are going to solve this using type aliases. Typescript has 3 ways of getting type information:

- A JS `class` has its own type information, and also is a valid constructor function usable though the application. You can both extend, and implement classes in other classes, notice you can implement many classes, but only can extend one.

- An `interface`. This is a TS feature that allows you to declare the _shape_ of an object. This doesn't exist in runtime, so you can't invoke it or assign it as a `class`, don't worry, TS will complain if you try to do something like that. You can extend both classes and other interfaces in an interface declaration. You can implement many interfaces in a `class`. 

- A `type`. This is another TS feature that allows you to declare the _shape_ of an object. This also doesn't exist in runtime, but you can't extend it and only can implement it if the `type` has statically known members. With a `type` you can use instead type modifiers, mappers, conditionals, and other complex typing structures, like _type aliases_.

So, a _type alias_ is a `type` that helps us to express better the shape of a complex object. How complex is `memo`? You may ask. It's actually kind of complex to express an object when you don't know all the properties it would have. The properties of `memo` are calculated in runtime, they are not _statically known_. Typescript has several built-in official helpers, but for some reason they are not listed in the official documentation. Some of those aliases are: `NonNullable`, `ReturnType`, `InstanceType`, `Readonly` and `Record`. The last one is the one we are going to use to fix our problem:


```ts
function fibonacci(num: number, memo: Record<number, number>): number {
  memo = memo || {};

  if (memo[num]) return memo[num];
  if (num <= 1) return 1;

  return (memo[num] = fibonacci(num - 1, memo) + fibonacci(num - 2, memo));
}
```

With `Record<number, number>` we are telling Typescript that `memo` is an object with `number` indexes and `number` properties. It checks it, and everything seems to be ok now.

## Compiling

We know the `tsc` command (or not) but what it does it just compile (technically [_transpile_](https://www.wikiwand.com/en/Source-to-source_compiler)) from Typescript to vanilla JavaScript while checking all that all the types are correct. By default, `tsc` will output the `.js` file regardless the types are correct or not. If you look at the output it looks more like the first version of our example, and that's ok. That's because no device can run TS source code, but JS, so what TS does is removing all our annotations and leaving a nice and clean JS file.

The power of the `tsc` is in its configuration file, [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) and its [Compiler Options](https://www.typescriptlang.org/docs/handbook/compiler-options.html). With that you can enable or disable powerful features, such no emits on errors or allow JS files to be compiled.

## Looking forward

As I said earlier, TS moves really fast. They recently introduced new ES features, as [Optional Chaining](https://github.com/microsoft/TypeScript/issues/16) and [Nullish Coalescing](https://github.com/microsoft/TypeScript/issues/26578), and also new TS features as [Recursive Type References](https://github.com/microsoft/TypeScript/pull/33050) and [Assertion Signatures](https://github.com/microsoft/TypeScript/pull/32695), and some new features looking to be introduced in TS 3.8 in February. Because of the breaking changes, frameworks that use TS (as Angular or React) can't go that fast, so they often offer support for a new version of TS a couple of weeks after it was released. But this is changing as TS now provides an RC version with a stable API. However, it doesn't stop you to play around with the new features in the [Playground](https://www.typescriptlang.org/play/).

## Bonus: Typing JavaScript

You can use JSDocs to give type annotations to variables and methods in JS files. Then, you can use a TS powered editor like VS Code to do all the checking and the power of TS in regular JS files. 

[1]: For the record, I got that function from here: https://medium.com/developers-writing/fibonacci-sequence-algorithm-in-javascript-b253dc7e320e. I ask no permission, and I hope for the author not to be mad about it. Thank you for sharing this. :smile:. I took it because Fibonacci functions look easy, but they are complex to type, as we had seen.