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 | undefinedassert.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): numberassert.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 | undefinedassert.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 avoid
return type in TypeScript. - A function that has a
never
return type never returns. It doesn't returnundefined
, 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(); // oklet 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!