Welcome to the beginning of our series on Typescript. In this series, we’ll be focusing on Types. So, sit back, relax, and enjoy the journey ahead.

What exactly is Typescript?

Typescript is a flavor/variant of Javascript, which means it is built on top of Javascript, extending its capabilities while maintaining compatibility with Javascript. Typescript is designed to check if the types of programs written in Javascript are correct before the programs are actually run.

Typescript helps spot errors by looking at the types of values being used in the code, which is usually called static type checking.

Types

Primitive Types

string – Represents string values like “Hello, TWC”
number – For numbers like 42
boolean – Represents values true and false

Arrays

There are two ways to represent arrays in Typescript: either you use – number[](an array of numbers), string[](an array of strings), or you write it as Array<number>, Array<string>.

any

You can use the “any” type when you want to be flexible with the data type of a value. When a value is of type “any,” you can access any properties of it, which will also be of type “any.”

let myObj: any = { x: 2 }; 
/* The following code will compile without any errors. */
myObj.submit();
myObj();
myObj.age = 120;
myObj = "Lola"
const name: string = myObj;

Remember that when you don’t specify a type in TypeScript and TypeScript can’t figure out the type from the context, it will default to “any.” It’s generally best to avoid using “any” because it doesn’t provide type checking. You can use the compiler flag “noImplicitAny” to treat any implicit uses of “any” as errors.

Type annotations on variables

You have the option to specify the type of a variable by adding a type annotation when using const, var, or let to declare it.

let name: string = "John";

Most of the time, you don’t have to worry about this. Typescript does its best to figure out the types in your code without you needing to specify them. For instance, it can determine the type of a variable based on the value you give it.

let name = "Doe";

Functions

In TypeScript, you can specify the types of both the input and output values of functions.

Parameter type annotations

When you create a function, you can specify the types of values it can work with by adding type annotations after each input. These annotations help clarify the kinds of data the function can handle.

function sayHi(name: string) {
  console.log("Hello, " + name.toUpperCase() + "!!");
}

Even if you don’t specify the types of information you’re working with, Typescript will still ensure that you provide the correct number of arguments.

Return type annotations

Return type annotations should be placed after the parameter list when writing functions in Typescript.

function favName(): string {
  return "Lola";
}

Just like adding annotations to your variables, in most cases, you don’t have to specifically mention the type of data that a function will return in TypeScript. TypeScript can usually figure out the return type of a function based on what the function returns. So, the type annotation in the example above doesn’t really make a difference.

Functions which return Promises

If you’re dealing with a function that returns a promise and want to specify the type of data to which the promise will eventually resolve, you should use the Promise type.

async function favName(): Promise<string> {
  return "Lola";
}

Object types

When we define an object type, we are basically just listing down the properties of the object and what type of properties they are.

const point: { a: number; b: number } = { a: 3, b:7 };

You can separate the properties using commas or semicolons; you don’t have to use a separator after the last property. Also, you don’t have to specify the type for each property; if you don’t, it will be assumed to be any.

Optional properties

Object types can also specify that some or all of their properties are optional. To do this, you can add a question mark (?) after the property name.

function printPetInfo(obj: { name: string; age?: number }) {
  // …
}

// Both will work as expected
printName({ name: "Lala" });
printName({ name: "Alice", age: 5 });

In JavaScript, if you try to use a property that doesn’t exist, you won’t get an error; you’ll get a special value called “undefined.” This means that if you want to use a property that might not be there, you have to check if it’s undefined before using it.

function printName(myObj: { first: string; last?: string }) {
  // This will crash if 'myObj.last' wasn't provided!
  console.log(myObj.last.toUpperCase());

  if (myObj.last !== undefined) {
    // This will not crash
    console.log(myObj.last.toUpperCase());
  }

  // A safe alternative using modern JavaScript syntax:
  console.log(myObj.last?.toUpperCase());
}

Union Types

A union type is a type that combines two or more other types, and it represents a value that can be any of those types. Each of the combined types is called a member of the union.

function printAge(age: number | string) {
  console.log("Your Age is: " + age);
}

// This is okay
printAge(120);

// This is okay
printAge("600");

// This will display an error
printAge({ myAge: 21 });

Type Aliases

A type alias is simply a name for any type. It’s like giving a nickname to a type so that you can easily use it in your code without having to type out the full type every time. It’s like creating a shortcut for a long and complicated word.

type Point = {
  x: number;
  y: number;
};

function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y)
}

printCoord({ x: 120, y: 120 });

You can create a type alias to give a name to any type, not just an object type. For instance, a type alias can be used to name a group of different types combined.

type ID = number | string;

Interfaces

When we talk about an interface declaration, we’re basically giving a name to an object type.

interface Point {
  x: number;
  y: number;
}

function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}

printCoord({ x: 100, y: 100 });

Differences between type aliases and interfaces

Type aliases and interfaces are similar in many ways, and you can use them interchangeably in most cases. Both type aliases and interfaces allow you to define the shape of data. However, the main difference is that once you create a type alias, you can’t add more properties to it later, while with an interface, you can always add more properties to it.

interface Window {
  title: string;
}

interface Window {
  ts: TypeScriptAPI;
}

// This will compile without errors
const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});

type Window = {
  title: string;
}

// This will compile with an error --> Error: Duplicate identifier // 'Window'
type Window = {
  ts: TypeScriptAPI;
}

Type assertions

Sometimes, in TypeScript, you might have information about the kind of data a value represents that TypeScript itself needs to be made aware of.

When you’re using the document.getElementById function in TypeScript, the program only recognizes that it will return an HTMLElement of some kind. However, you might already know that your page will always have an HTMLCanvasElement with a specific ID. In this case, you can use a type assertion to tell TypeScript that you’re confident the returned element will be of a more specific type.

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

Typescript only allows type assertions that convert to a more specific or less specific version of a type. This rule prevents making “impossible” changes to types.”

const x = "hello" as number;

Sometimes, the rule can be too strict and might not allow for more complex conversions that could actually be correct. If this happens, you can use two assertions: first to “any” or “unknown” and then to the type you actually want.

const a = expr as any as T;

Literal types

Besides the general categories of strings and numbers, we can also talk about specific words and numerals when we’re working with different types of data.

function printText(s: string, alignment: "left" | "right" | "center") {
  // …
}
printText("Hello, world", "left");
/* This next line will print an error --> Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'. */
printText("G'day, mate", "centre");

function compare(a: string, b: string): -1 | 0 | 1 {
  return a === b ? 0 : a > b ? 1 : -1;
}

Enums

Enums in JavaScript, introduced by TypeScript, let you describe a value that can be one of a few specific options.

enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}

let move: Direction = Direction.Up;
console.log(move);  // Output: 1

enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT"
}

let move: Direction = Direction.Up;
console.log(move);  // Output: "UP"

The post covered important concepts such as primitive types, arrays, functions, object types, and union types. I encourage you to explore TypeScript’s type system further to enhance your understanding of writing robust and error-free code via the official Typescript documentation website on types https://www.typescriptlang.org/docs/handbook/2/everyday-types.html.

Hope you enjoyed reading. Don’t forget to share!

Categorized in: