Typescript types

Yasemin çidem
5 min readFeb 17, 2023

--

In this article, i’d like to pick up where i left off in this article. At the end of the article, we will have gone through all the basic types in typescript. Let’s start with enum type:

Enum:

Before diving deep into enum type, i’d like to briefly introduce enum type to you. An enum is a group of named constant values. If you want to collect constant values in one place, it would be great to apply for enum . You can define both numeric and string-based enums. I will leave couple of examples with their translation in js runtime.

// typescript number enum example
// all the enum values will be automatically increased by one
// either when you leave off intializers or start with number initializer
enum Direction {
Up = 1, // initializers can be left off
Down,
Left,
Right,
}
const directionUp = Direction.Up // the value of directionUp is 1
const directionDown = Direction.Down // the value of directionDown is 2
const directionLeft = Direction.Left // the value of directionLeft is 3
const directionRight = Direction.Right // the value of directionRight is 4

// javascript
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 1] = "Up";
Direction[Direction["Down"] = 2] = "Down";
Direction[Direction["Left"] = 3] = "Left";
Direction[Direction["Right"] = 4] = "Right";
})(Direction || (Direction = {}));
// typescript string enum example
// string enums don’t have auto-incrementing behavior
// they show the given initialized string
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
const directionUp = Direction.Up // the value of directionUp is UP
const directionDown = Direction.Down // the value of directionDown is DOWN
const directionLeft = Direction.Left // the value of directionLeft is LEFT
const directionRight = Direction.Right // the value of directionRight is RIGHT

// Javascript
var Direction;
(function (Direction) {
Direction["Up"] = "UP";
Direction["Down"] = "DOWN";
Direction["Left"] = "LEFT";
Direction["Right"] = "RIGHT";
})(Direction || (Direction = {}));

Since we know how to use enum, i’d like to emphasize couple of important issues about enum.

Let’s tackle the problem with number enums:

enum Direction {
Up = 1, // initializers can be left off
Down
}
const getDirection = (direction: Direction): void => console.log(direction)

getDirection(1) // valid
getDirection(2) // valid
getDirection(29) // valid (it shouldn't be)

Which shows that, numeric enum is not gonna prevent you from passing any number in. It is one thing we should be aware of when we are using numeric enum.

Now let’s look at the issue in string enum:

I’d like to show that how enums works a bit different than other types in Typescript considering that Typescript operates with shape or structure based typing in other words as long as the the shape of your object matches the shape of your type, Typescript consider it valid.

For those of you who don’t know structure based typing, i’d like to give an example to give you a better idea about it. Structural typing is a way of relating types based solely on their members.

interface Pet {
name: string;
}
class Dog {
name: string;
}
let pet: Pet;
// The reason why ts compiler doesn't complain is
// because of structural typing
// it is always valid in case structures are aligned with one another.
pet = new Dog();

With that said, let’s tackle the string enum example one more time.

enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
// direction is UP and it is valid
const direction: Direction = Direction.UP

// it is not valid even though the structure is same
const direction2: Direction = "UP"

It seems like structural typing doesn’t align with the way how string enum works . This code example should be valid considering Direction.UP and UP shows the same value in other words they are structually equal.

These are the drawbacks of using enums. But it doesn’t mean that we should avoid enum at any cost. I don’t think it would cause any problem if we are aware of these points. If i have to share my personal choice, i’d say that i would rather use Union .

Unknown:

There are times we may need to describe the type of variables that we do not know. We let compiler know that this variable could be anything by annotating unknown type. Here are couple of examples to show you how to specify unknown type.

let notSure: unknown = 4;
notSure = "maybe a string instead";

// OK, definitely a boolean
notSure = false;

Looking at the definition brings to mind this question. Isn’t any type working the same way ? Let’s find out.

Any:

Any type can be used in the same way as unknown the times when we don’t know what type to use which brings us to the question. What distinguishes any from unknown ?

  1. ) any type allows you to gradually opt-in and opt-out of type checking during compilation which is considered as bad practice in Typescript.
  2. ) unlike unknown , any allows you to access all properties. When you use any , you are basically telling typescript compiler not to give you trouble and it provides you all the properties even they don’t exist in your type.
let looselyTyped: any = 4;
// OK, sssss doesn't exist (there is no complaint from the compiler)
looselyTyped.sssss();
// OK, toFixed exists (but the compiler doesn't check)
looselyTyped.toFixed();

let strictlyTyped: unknown = 4;
strictlyTyped.toFixed();
// Object is of type 'unknown'.

As you can see, this is a kind of type which we have to use as a last resort.

Void:

void is the absence of having any type at all. This type commonly used as return type of functions. When you see this type be used in any function, you can assume that there will be no value returned from function.

function warnUser(): void {
console.log("This is my warning message");
}

Null and undefined:

I wanted to cover two types in here even though they are not useful on their own. You can easily tell that what is the point of using them by looking at the following example:

// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;

The reason i wanted to introduce these types is because they will come in handy in union types which is a type that we will cover in a bit.

Union:

We seen most of the built-in types in typescript so far. Now, it is time to ask this question. What if i want to create my own type ? Well, be ready to learn advanced types then. Let’s see what typescript’s type system tells us about this question.

Typescript’s type system allow us to build new types out of existing ones using a large variety of operators. One of the way to combine types you might guess is a union type. A union type is a type formed from two or more other types.

let id: (number | string) = 0;
id = 100; // correct
id = "100"; // correct
id = {myId: "ssss"} // incorrect
let id: (number | string) = 0;
id.toUpperCase() // compiler will show an error here

I think, the second example is a worth a mention since it will be misleading at first glance. Even though, id might be a number and string , whey ts compiler compiles about a property of string. It’s because union type only allows us to get access to the intersection of the properties of the given types. When we try to get access to toString , ts compiler will not complain because this property exists in both given types.

let id: (number | string) = 0;
id.toString()

I believe that we cover most of the basic types so far. In next article, i will tackle more advanced types.

--

--

No responses yet