TypeScript

Learn TypeScript in less than 10 minutes

You won't be TypeScript expert but it will give you all you need to code average TypeScript code

TypeScript is just a superset of JavaScript meaning everything that you write in JavaScript will work in TypeScript.

TypeScript is a superset of JavaScript

Think about it like JavaScript with the option to add types as you go.

NOTE: I intentionally remove explanation on Enums and Decorators, because I don’t think they are useful (for Enums I prefer to use object literals, and Decorators are unstable JavaScript feature that implemented wrong in early TypeScript versions).

There are 2 things that you need to learn in order to understand TypeScript: Types / Interfaces + Config.

Types & Interfaces

TypeScript is using duck typing for types and interfaces. Duck typing in computer programming is an application of the duck test— “If it walks like a duck and it quacks like a duck, then it must be a duck” — to determine if an object can be used for a particular purpose. With normal typing, suitability is determined by an object’s type. In duck typing, an object’s suitability is determined by the presence of certain methods and properties, rather than the type of the object itself.

Types

Specifying types to variables / functions made by adding : TypeName after the declaration.

For example:

const message: string = 'hello world';

is like telling TypeScript: “message has a type of string”.

Basic Types

  • boolean (true / false)
  • number (1, 0.3, -199)
  • string (“Hello World”)
  • null
  • undefined
  • void (return value of function that does not expected to return something. Usually I’m not write it explicit and I prefer TypeScript will infer it by itself)
  • object (less popular. better to specifying object type explicit like PersonT = {firstName: string, lastName: string} )
  • never (less popular - function that never returns like infinity loop / throw returns never )

Any type

any means - ignore type checking.

Known bad practice is to use this type everywhere when they don’t know how to specify type correctly. By doing so - they miss the reason why using TypeScript at all. You want this type-checking. This is the whole purpose of TypeScript. You want the TS compiler will warn you and you want the auto-completion.

For example:

let a: any = 'HI';
a = 5; // everything is okay
a = {}; // no error, no warning, I can do wherever I want!

TypeScript won’t tell you that you are doing something weird because you declared a to be any.

Better use-case will be in “force” conversion.

const a = (getUnknownVariable() as any) as SomeKnownType;

This example is like saying “Hi TypeScript, I know what am I doing, so ignore type checking for the return value of getUnknownVariable() and convert it to SomeKnownType - so a will be in SomeKnownType type.

But sometimes, any type can be useful and correct. If you look at globals.d.ts (global environment type definitions) you will see

/**
 * Prints to `stdout` with newline.
 */
log(message?: any, ...optionalParams: any[]): void;

this is console.log type definitions and it does not care what message will be using any.

Unknown type

unknown is like the opposite of any. It like saying “I don’t know what the type is so I want TypeScript will tell me to convert this type every time I use it”. The unknown type is only assignable to any type and the unknown type itself.

const valueFromServer: unknown = 'Hello World';
// Error: Type 'unknown' is not assignable to type 'string'
const stringMessage: string = valueFromServer;

TypeScript expect me to convert unknown type explicit

const valueFromServer: unknown = 'Hello World';
// stringMessage can be use as string
const stringMessage = valueFromServer as string;

Arrays / Tuples

Arrays of type - meaning all the items in the array has the same type

const arr = [1, 2, 3]; // arr: number[]
const arrExplicit1: number[] = [1, 2, 3]; // arr: number[]
const arrExplicit2: Array<number> = [1, 2, 3]; // arr: number[]

Tuples are like defining variables one after one inside an array (popular in destructuring) I think on tuples like a set of variables combined in the array. Each item can have its own type.

function getTuple(a: number, b: string) {
  /**
   * `as const` tell TypeScript to infer [number, string]
   * instead of [number | string, number | string].
   * but you can explicitly write
   * `return [a,b] as [number, string]`
   */
  return [a, b] as const;
}

/**
 * getTuple will return `[number, string]`
 * that means that the first element of the
 * array will be number and the second is a string
 */
const [num, str] = getTuple(5, 'string');

You can combine it with the …rest operator

const t: [number, string, ...number[]] = [1123989012301, 'David', 1, 2, 3, 4];

Objects

Defining objects in TS is like specifying their fields one by one

type PersonT = {
  name: string;
  /**
   * ? stands for "optional"
   * in this case - allow PersonT
   * without childrenNames key
   * or with undefined childrenNames value is ok
   */
  childrenNames?: string[];
};

// Valid objects:

const objectWithoutKey: PersonT = {
  name: 'Nir',
};

const objectWithUndefinedKey: PersonT = {
  name: 'Nir',
  childrenNames: undefined,
};

const objectWithEmptyArray: PersonT = {
  name: 'Nir',
  childrenNames: [],
};

const objectWithArrayOfStrings: PersonT = {
  name: 'Nir',
  childrenNames: ['Tamir'],
};

Const Assertion (“as const”)

When we construct new literal expressions with const assertions, we can signal to the language that

  • No literal types in that expression should be widened (e.g. no going from “hello” to string).
  • Object literals get readonly properties.
  • Array literals become readonly tuples.
// str type is "HI" instead of a string
const str = 'HI' as const;

/**
 * obs type is {readonly a: 1; readonly b: 2;}
 * instead of {a: number; b: number;}
 */
const obs = {
  a: 1,
  b: 2,
} as const;

// arr type is [1,2] instead of [number, number]
const arr = [1, 2] as const;

Interfaces

interface is very similar to type.

interface IDog {
  name: string;
  heightInCM: number;
}

type DogT = {
  name: string;
  heightInCM: number;
};

Differences:

  • interface can be extended - meaning create interface with props of other interfaces

    interface ICoolDog extends IDog /*you can extend more than one interface*/ {
    // you can leave it empty
    coolName: string | null; // can be string or null
    }
    
    const coolDog: ICoolDog = {
    name: 'dog',
    heightInCM: 59,
    isCool: true,
    coolName: 'doggy',
    };
  • types can be primitives:

    // cannot be done in interfaces
    type IdT = string;
  • type union manipulation (typeA or typeB):

    const id: IdT | null = isNew ? null : uuid();
  • classes can extend interfaces and not types

    IClass {
    state: string
    }
    class A extends IClass {
    state = "a"
    }

    Type Operators

My rule of thumb if it can be interface - make it interface, else - make it type.

Function types

For regular functions you just put the types of the params and the return types. For example:

function doSomething(p: IParam): number /* return type */ {
  return 1;
}

function noop(): void /* return type void means the function does not return value */ {}

For arrow function:

const doSomething: () => number = () => {
  return 1;
};

const noop: () => void = () => {
  return 1;
};

Also function be represent as object param:

interface IDog {
  firstName: string;
  bark: () => void;
}

Config

In order to use TypeScript in any JavaScript environment, you need to convert the TypeScript code to JavaScript code. The output looks like this example I created in TypeScript Playground

TypeScript code converted to JavaScript code

This is the main barrier to add TypeScript to your project. Luckily in 2020, it’s easier than the past. create-react-app, next.js and react native (bare / expo) supports TypeScript out of the box with no additional configuration. Also Parcel (it’s not fair to say because this is the main idea of parcel), and even the arising Pika, Snowpack and Vite bundlers. Angular users probably tied into TypeScript and also Vue users can use typescript (vue refactor its codebase from FlowType to TypeScript). For node.js you can use ts-node (or transpile node.js code with babel). For creating npm libraries use tsdx. And for the future maybe you will use Deno.

It may be a bit annoying to config webpack (or your other bundler) to transpile TypeScript, but there are a lot of examples around the internet of how to do it. And if you are already using babel so it’s some up to add only one plugin (@babel/preset-typescript). Now babel can transpile TypeScript, and it does it well so I recommend using it instead of typescript tsc command.

One thing every TypeScript project need is tsconfig.json file, specifying TypeScript options. In the above boilerplate, you get one for free. You can also run tsc --init and generate it yourself, or search in the web for existing tsconfig.json file that specifies your environment needs (browser, node, react-native). Tip: take a look at tsconfig/bases.

Specify Types

Types can be added in your TypeScript code.

If you rely on JavaScript code, you can add `module.d.ts` file in order to add TypeScript declarations.

If you are using 3rd party libraries like react, that does not supply TypeScript definitions or have not written in TypeScript, you may able to add TypeScript declarations from DefinitelyTyped (if they exist)

npm install --save-dev @types/your-3rd-party-library-name

If there are no declarations there and your library is written in TypeScript you still can write TypeScript definitions file for this library yourself. Last resort is to use it and specify its type as any - NOT RECOMMENDED.

Gradual Migration From JavaScript

If you already have a project written in JavaScript, it’s still strongly recommended to migrate to TypeScript. You shouldn’t throw all your code. You just need to change .js / .jsx files to .ts / .tsx file endings, and you are on. You can add types gradually. Most of the feedback from people that does it is “We detected some bugs because TS compiler said we use this variable like it was X, but it was actually Y”.

Subscribe to Nir Tamir

Get the latest posts delivered right to your inbox