any
and unknown
In TypeScript, any
and unknown
are types that contain all values. In this chapter, we examine what they are and what they can be used for.
any
and unknown
are so-called top types in TypeScript. Quoting Wikipedia:
The top type […] is the universal type, sometimes called the universal supertype as all other types in any given type system are subtypes […]. In most cases it is the type which contains every possible [value] in the type system of interest.
That is, when viewing types as sets of values (for more information on what types are, see “What is a type in TypeScript? Two perspectives” (§13)), any
and unknown
are sets that contain all values.
TypeScript also has the bottom type never
, which is the empty set and explained in “The bottom type never
” (§15).
any
If a value has type any
, we can do everything with it:
function func(value: any) {
// Only allowed for numbers, but they are a subtype of `any`
5 * value;
// Normally the type signature of `value` must contain .propName
value.propName;
// Normally only allowed for Arrays and types with index signatures
value[123];
}
Every type is assignable to type any
:
let storageLocation: any;
storageLocation = null;
storageLocation = true;
storageLocation = {};
Type any
is assignable to every type:
function func(value: any) {
const a: null = value;
const b: boolean = value;
const c: object = value;
}
With any
we lose any protection that is normally given to us by TypeScript’s static type system. Therefore, it should only be used as a last resort, if we can’t use more specific types or unknown
.
JSON.parse()
The result of JSON.parse()
depends on dynamic input, which is why the return type is any
(I have omitted the parameter reviver
from the signature):
JSON.parse(text: string): any;
JSON.parse()
was added to TypeScript before the type unknown
existed. Otherwise, its return type would probably be unknown
.
String()
The function String()
, which converts arbitrary values to strings, has the following type signature:
interface StringConstructor {
(value?: any): string; // call signature
// ···
}
noImplicitAny
If the compiler option noImplicitAny
is true
, TypeScript requires explicit type annotations in locations where it can’t infer types. The most important example is parameters definitions. If this option is false
, it (implicitly) uses the type any
in those locations.
This is an example of a compiler error that is caused by noImplicitAny
being true
:
// @ts-expect-error: Parameter 'name' implicitly has an 'any' type.
function hello(name) {
return `Hello ${name}!`
}
assertType<
(name: any) => string
>(hello);
TypeScript does not complain about us using the type any
explicitly:
function hello(name: any) {
return `Hello ${name}!`
}
unknown
The type unknown
is a type-safe version of the type any
. Whenever you are thinking of using any
, try using unknown
first. unknown
is similar to any
in that we can assign any value to it:
let storageLocation: unknown;
storageLocation = null;
storageLocation = true;
storageLocation = {};
However, an unknown
value is not assignable to anything:
function func(value: unknown) {
// @ts-expect-error: Type 'unknown' is not assignable to type 'null'.
const a: null = value;
// @ts-expect-error: Type 'unknown' is not assignable to type 'boolean'.
const b: boolean = value;
// @ts-expect-error: Type 'unknown' is not assignable to type 'object'.
const c: object = value;
}
Therefore, if we have a value of type unknown
, we must narrow that type before we can do anything with the value – e.g., via:
function func(value: unknown) {
// @ts-expect-error: 'value' is of type 'unknown'.
value.toFixed(2);
// Type assertion:
(value as number).toFixed(2); // OK
}
Equality:
function func(value: unknown) {
// @ts-expect-error: 'value' is of type 'unknown'.
value * 5;
if (value === 123) { // equality
assertType<123>(value);
value * 5; // OK
}
}
function func(value: unknown) {
// @ts-expect-error: 'value' is of type 'unknown'.
value.length;
if (typeof value === 'string') { // type guard
assertType<string>(value);
value.length; // OK
}
}
function func(value: unknown) {
// @ts-expect-error: 'value' is of type 'unknown'.
value.test('abc');
assertIsRegExp(value); // assertion function
assertType<RegExp>(value);
value.test('abc'); // OK
}
/** An assertion function */
function assertIsRegExp(arg: unknown): asserts arg is RegExp {
if (! (arg instanceof RegExp)) {
throw new TypeError('Not a RegExp: ' + arg);
}
}