For further details, check out "The object
Type in TypeScript".
The object
type introduced in TypeScript serves the purpose of excluding the seven primitive types, such as string
, number
, boolean
, bigint
, symbol
, undefined
, and null
. Even though
typeof null === "object"
at runtime, it remains a primitive in both JavaScript (JS) and TypeScript (TS). While values like
string
,
number
,
boolean
,
bigint
, and
symbol
are automatically wrapped in respective objects (
String
,
Number
,
Boolean
,
BigInt
,
Symbol
) when accessing their members as if they were objects, they are distinguishable from true objects, making a difference in certain scenarios. One example mentioned in the TypeScript Handbook is
Object.create()
, which generates runtime errors when passed an argument of a primitive type (aside from
null
). Therefore, for functions like
Object.create()
, TypeScript specifies that its argument should be of type
object | null
. To exclude primitives in a generic parameter, using
<P extends object>
is the correct approach, proving its significance.
Note that TypeScript also includes an interface named Object
, starting with an uppercase O
, containing apparent members that exist on everything in JS, such as valueOf()
and toString()
. Although this interface might align more closely with the concept of "everything is an object," only null
and undefined
cannot be assigned to Object
. Generally, opting not to utilize the Object
type in TypeScript, as wrapper types are rarely preferred by developers, is advisable.
In case you aim to encompass "anything that can be indexed into like an object," employing the so-called "empty object" type, {}
, is recommended. This object type possesses no known properties and operates similarly to Object
, with only null
and undefined
being exceptions. It was previously the case that unconstrained generic type parameters were implicitly constrained to {}
, hence rendering <P extends {}>
virtually arbitrary.
However, since TypeScript 3.5, unconstrained generics now receive an implicit constraint of unknown
instead of {}
. The unknown
type truly embodies "everything" in TypeScript. Basically, any value can be assigned to a variable of type
unknown</code (though vice-versa isn't permissible). Specifying <code><P extends unknown>
turns redundant due to this inclusiveness.
Lastly, we point towards any
, representing the ultimate "anything-goes" type. Not only does any
allow assigning any value (akin to
unknown</code), but it also permits assigning <code>any
itself to anything (like
never</code). Resorting to <code>any
suggests surrendering to bypass type checking rather than embracing a genuine type. Since TypeScript 3.9, utilizing
<P extends any>
equals to working with
<P extends unknown>
, rendering them equally superfluous. The prior functionality of allowing
P
to resemble
any
under unresolved circumstances through
<P extends any>
was considered impractical and thus modified.
Here's the playground link to access the code.