Four Comparison in TypeScript to Understand Basic Types Better

Optional vs. Default value vs. Undefined; Null vs. Undefined; Never vs. Void; Any vs. Unknown

E.Y.
4 min readMar 27, 2021
Photo by Raychan on Unsplash

In this blog, we will go through some basic and advanced types comparison in TypeScript which will help you better type in the code.

Optional vs. Default value vs. Undefined

If you first know about TypeScript, it’s easy to get confused about the following options :

  • Parameter is optional: x?: number
  • Parameter has a default value: x = 123
  • Parameter has a union type with undefined: x: undefined | number

Let’s go through them one by one:

1. With optional parameter, the type will be undefined if param omitted:function f1(x?: number) { return x }
// type ======>
// function f1(x?: number | undefined): number | undefined
assert.equal(f1(123), 123);
assert.equal(f1(undefined), undefined);
assert.equal(f1(), undefined);
2. With default parameter, the type value will be the default type if param is omitted, function f2(x = 123) { return x }
// type ======>
// function f2(x?: number): number
assert.equal(f2(123), 123);
assert.equal(f2(undefined), 123);
assert.equal(f2(), 123);
3. With a union type including undefined, the param cannot be omitted:function f3(x: undefined | number) { return x }
// type ======>
// function f1(x?: number | undefined): number | undefined
assert.equal(f3( 123), 123);
assert.equal(f3(undefined), undefined);
f3(); //Expected 1 arguments, but got 0.

Null vs. Undefined

In TypeScript, both undefined and null actually have their types named undefined and null respectively.

By default null and undefined are subtypes of all other types. So we can assign null and undefined to number.

However, with--strictNullChecks flag, null and undefined are only assignable to unknown, any and their respective types (the one exception being that undefined is also assignable to void).

When it comes to the differences between Null and Undefined, we have to come down to JavaScript.

- Undefined means a variable has been declared but not yet assigned var test;
alert(test); // undefined
alert(typeof test); // undefined
- Null is an assignment value. It can be assigned to a variable as a representation of no value. Note that its type is ACTUALLY OBJECT!!!var test = null;
alert(test); // null
alert(typeof test); // object
=================================>null === undefined // false
null == undefined // true
null === null // true

Never vs. Void

The never type represents the type of values that never occur. Use cases for never :

  • A function never returns (e.g. while(true){})
  • A function always throws (e.g. function foo(){throw new Error('Not Implemented')} )

As an example:

let bar: never = (() => { throw new Error(`Throw my hands in the air like I just don't care`) })();

It is also useful for exhaustive checks:

function foo(x: string | number): boolean {
if (typeof x === "string") {
return true;
} else if (typeof x === "number") {
return false;
}
return fail("Unexhaustive!");
}
function fail(message: string): never { throw new Error(message); }

Note that never type is a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself). Even any isn’t assignable to never.

How about void?

A function that returns nothing returns a Unit void. It has a value, which is undefined or null with strictNullChecks off.

let unusable: void = undefined;
// if` — strictNullChecks` is not given
unusable = null;

So what is the differences?

In short, void returns void, never never returns.

As quoted from Marius Shultz here:

  • A function that doesn’t explicitly return a value implicitly returns the value undefined in JavaScript. Although we typically say that such a function "doesn't return anything", it returns. We usually ignore the return value in these cases. Such a function is inferred to have a void return type in TypeScript.
  • A function that has a never return type never returns. It doesn't return undefined, either. The function doesn't have a normal completion, which means it throws an error or never finishes running at all.

Any vs. Unknown

The any type represents all possible JS types. Every type is assignable to type any. We label values with the any type to bypass the TypeScript check.

declare function getValue(key: string): any;
const str: string = getValue("myString");

The unknown type also represents all possible JS types. Every type is assignable to type unknown(same as any). However, the TS compiler won't allow any operation on unknown, and unknown type is only assignable to the type any. Any operation on unknown without a type assertion or a control flow based narrowing is not permitted.

let myVar: unknown;let myVar1: unknown = myVar;   // OK
let myVar2: any = myVar; // OK
let myVar3: boolean = myVar;
// Type 'unknown' is not assignable to type 'boolean'
// The following operations on myVar all give the error:
// Object is of type 'unknown'
myVar[0];
myVar();
myVar.length;
new myVar();
declare const maybe: unknown;
if (maybe === true) {
const aBoolean: boolean = maybe;
const aString: string = maybe;
// Error : Type 'boolean' is not assignable to type 'string'.
}

Unlike unknown, variables of type any allow you to access arbitrary properties, even ones that don’t exist.

let looselyTyped: any = 4;
looselyTyped.ifItExists(); // ok
let strictlyTyped: unknown = 4;
strictlyTyped.toFixed();
// ERROR: Object is of type 'unknown'.

But this comes at the cost of running into runtime errors, which we should always try to avoid and use unknown maybe instead.

That’s so much of it!

Happy Reading!

--

--

No responses yet