Basic Types in TypeScript

In the vast ocean of programming languages, TypeScript has emerged as a beacon of type safety for JavaScript developers. Whether you are new to this ecosystem or are a seasoned JavaScript professional, TypeScript’s robust type safety system can surely transform the way you code. In this post, we’ll look at the basic types that TypeScript provides us. We won’t go into the advanced types like unions, intersection, type aliases, etc. as I believe they deserve a separate blog themselves.

But before getting started, you might be wondering why even it’s necessary. I mean, JS has survived on its own for such a long time, and then this TS thing comes claiming itself to be the superset of JS! Now, I won’t go into the history and everything but let me tell you an advantage of TS.

Since, in TS you can explicitly assign types, it allows you to catch errors during development itself, since it checks types during compilation. You would get those squiggly lines if you tried to do something wrong.

And that can save a ton of time as you do not get some surprise error during production. Prevention is better than cure! And there are many other advantages that TS offers, but that is not the goal of this blog. With that let’s start.. finally!

Number

It’s the same as in JS. Whether it is a float, decimal, or hexadecimal, it's a number. TS also supports octal and binary.

let num1: number = 6;
let num2: number = 7.7;
let hexNum: number = 0xf00d;

let n1 = 5; //ok
n1 = "Hello, World!"; // error

For n1, I haven’t explicitly mentioned that it of type number. But when I try to assign some string value, TS yells at us. This is because of something called Type Inference. We could explicitly assign types, but if we are assigning values, TS automatically guesses it.

String

The string is used for working with textual datatypes.

let fullName: string = `Gon Freecss`;
let age: number = 12;
let description: string = `Hello, my name is ${fullName}.

I'll be ${age + 1} years old next month.`;

Boolean

Used to denote true/false values.

let isComplete: boolean = false;

Array

TypeScript, like JavaScript, allows you to work with arrays of values. Array types can be written in two ways.

let nums: number[] = [1,3,4,56,6];

// generic array type, Array<elemType>
let hobbies: Array<string> = ["cricket", "hiking"];

Tuple

Tuple types allow you to express an array with a fixed number of elements whose types are known, but need not be the same. e.g. you might want to represent a value as a pair of string and number.

let role: [number, string];
role = [2, "author"]; //ok
role = ["author", 2]; //not ok

role.push(11);
role.push("admin");

console.log(role[2]); //Error: Tuple type '[number, string]' of length '2' has no element at index '2'.
role[3] = "clerk"; //Error

However .push() method still works on tuples since tuples in TypeScript extend array functionality. But, if you try to access an element outside the defined tuple structure, TypeScript will throw an error.

Tuples can be useful when a function needs to return multiple values of different types, or when you want to represent a value with multiple parts.

Enum

Enums in TypeScript provide a way to define a set of named constants.

enum Role {
  ADMIN, //0
  READ_ONLY, //1
  AUTHOR //2
}

let r: Role = Role.ADMIN;

By default, enums begin numbering their members starting at 0. You can change this by manually setting the value of one of its members. For example, we can start the previous example at 1 instead of 0.

enum Role {
  ADMIN = 1, 
  READ_ONLY, //2
  AUTHOR //3
}

By using enums, you can reduce the chance of invalid values being introduced. e.g. if you have some entity that can be in one of the several states like pending, delivered, etc., instead of manually assigning those values, you should use enums. It makes the code readable and also reduces the chances of any invalid values being assigned. And of course, you get auto-completion in VS code 😜.

Any

Using any, you can store any type of values. When you use ‘any’, you're opting out of type checking for that part of your code. You’re essentially telling the TS compiler, “Trust me, I know what I’m doing. I got this!”.

You can perform any operation, access any property without any compile time errors.

let name: any;
name = "gon";
name = name.toUpperCase(); //accessing any property 
console.log(name);

name = 12; // no error

Only use any when you’re genuinely unsure about the type and are willing to skip the type checks. Overuse of any can negate the benefits of TS.

Unknown

Unknown type represents any value just like any, but you can't perform any operations on a value of type unknown without first asserting or narrowing its type. Let me show you what I mean by that.

As you can see, TS does not allow us to access any property without first confirming its type. Unknown is a safer alternative to any. You are essentially telling the TS compiler, “I don’t know the type of this value yet, but I want to ensure type safety before I use it.” Generally, if you can avoid any and lean towards unknown (in case you don’t know the type), you code will be more robust.

Void

Void is the opposite of any, i.e the absence of having any type at all. You will see this type in functions which do not return anything.

function greet(msg: string): void {
  console.log(`Hello, ${msg}`);
}

Null and Undefined

Undefined represents the absence of a value. In JS and TS, a variable that has been declared but has not been assigned a value is undefined.

let x: number;
console.log(x); // Outputs: undefined

function print(name?: string) { //optional parameter
    console.log(name);
}

print(); // Outputs: undefined, as we haven't passed anything

Null represents an intentional absence of an object value. Often used to represent “no value” or “unknown.”

let y: string | null = null;

Null is typically used to denote that a variable should have no value, especially in cases where you might expect an object or a value.

Never

The ‘never’ type represents the type of values that never occur. e.g. never would be the return type of a function that always throws an exception, or which runs infinitely (I know, I mean why would anyone write such a function 😂).

function throwEror(msg: string): never {
  throw new Error(msg);
}

function runTillEternity(): never {
  while (true) {}
}

Ok..! Unknown, null, undefined, never, void.. they might be confusing at first but you’ll get used to them eventually. These were the basic types in TS. We do have more advanced types as I mentioned like unions, intersection, and type aliases, but that is for another blog.

Also, we have much to discuss in TS like interfaces, classes, generics, type guards, type casting, modules, and even the TS Compiler! I’ll try my best to cover those topics soon.

Thank you for staying till the end!