TypeScript has two top types - any and unknown.
Top type is a supertype of every other type, which means that we can assign anything to it.
In the next few sections, we will familiarise ourselves with both keywords, identify the most important differences between them, and find out when we can use each.
Sometimes we want to describe a variable we do not know the type of.
That is exactly why we need any type.
It represents every possible value:
let a: any;
a = 1;
a = "string";
a = [];
a = {};
a = null;
a = undefined;
We are allowed to do any action on a variable of this type, which means that the following operations are all correct:
const a: any;
a.b.c.d.e.f;
a.toString();
a();
a[0];
Using it allows us to escape from the type system and write code as if it was pure JavaScript, which gives us a lot of freedom.
But writing code this way leads to some unpredictable errors that TypeScript can easily prevent.
Just like with any, we use the unknown type to describe a variable we do not know the type of:
let a: unknown;
a = 1;
a = "string";
a = [];
a = {};
a = null;
a = undefined;
All assignments are correct, but let's see what happens if we try to assign a variable of type unknown to any other type?
const a: unknown;
const b: number = a; // Error: Type "unknown" is not assignable to type "number".
const c: string = a; // Error
const d: string[] = a; // Error
const e: object = a; // Error
const f: null = a; // Error
// But
const j: unknown = a; // Looks good!
const e: any = a; // Looks good!
The unknown type is only assignable to the any type and itself.
Now, let's see if we can perform the same operations as on any type:
const a: unknown;
a.b.c.d.e.f; // Error: Object is of type "unknown"
a.toString(); // Error
a(); // Error
a[0]; // Error
None of these operations are allowed anymore.
By switching from any to unknown we reduced the number of available actions from all possible to almost nothing.
We can not perform any operations on the unknown type without narrowing it down.
There are a few ways to narrow down the unknown type, like: type assertions, typeof/instanceof operators.
const a: unknown = "STRING";
// Perform type assertion
const b: string = a as string;
console.log(b.toLowerCase()); // Prints "string"
Important note: be aware that TypeScript would not check whether your assertion is correct, and think you know better, therefore the following error is likely to happen:
const a: unknown = 1;
// Perform WRONG type assertion
const b: string = a as string;
console.log(b.toLowerCase()); // TypeError "b.toLowerCase" is not a function
In the example above b
contains a number, but TypeScript receives an assertion it is a string and and error is thrown, when we try to access toLowerCase
function on number.
const getValueType = (value: unknown) => {
if(typeof value === "string") {
return "String";
}
if(typeof value === "number") {
return "Number";
}
return "Unknown";
}
console.log(getValueType(10)); // Prints "Number"
console.log(getValueType("string")); // Prints "String"
console.log(getValueType(false)); // Prints "Unknown"
const getDateISOString = (value: unknown) => {
if(value instanceof Date) {
return value.toISOString();
}
return "Not a date";
}
console.log(getDateISOString(10)); // Prints "Not a date"
console.log(getDateISOString("string")); // Prints "Not a date"
console.log(getDateISOString(new Date())); // Prints "2020-08-10T18:51:51.673Z"
In a union type, unknown absorbs every type.
If any of the constituent types is unknown, the union type evaluates to unknown:
type A = unknown | number; // uknown
type B = unknown | string; // uknown
type C = unknown | boolean; // uknown
// But
type D = unknown | any; // any
Important note: if union type contains any, it evaluates to any.
In an intersection type, unknown is absorbed by every type.
Any type which intersects with unknown, does not change:
type A = unknown & number; // number
type B = unknown & string; // string
type C = unknown & boolean; // boolean
// But
type D = unknown & any; // any
Important note: if intersection type contains any, it evaluates to any.
In TypeScript, there are 2 top types: any and unknown.
They are both very similar, but unknown is much less permissive.
We need to do some type checking before we do anything with a variable of unknown type, which makes it more type-safe and preferred way to say that the type value is not known.
Try to avoid any as much as possible.