See "The object
Type in TypeScript" for more information.
The object
type in TypeScript was introduced specifically to exclude the seven primitive types, string
, number
, boolean
, bigint
, symbol
, undefined
, and null
. (Yes, typeof null === "object"
at runtime, but it is still considered primitive in JS and TS). It is true that string
, number
, boolean
, bigint
, and symbol
values will be automatically wrapped in String
, Number
, Boolean
, BigInt
, and Symbol
objects (respectively) when you access members on them as if they were objects. But they are distinguishable from true objects, and sometimes this makes a difference. The example given in the TypeScript Handbook is the Object.create()
, which leads to runtime errors if passed an argument of a primitive type (except for null
). Hence TypeScript's typing for Object.create()
specifies that its argument is of type object | null
. If you want your generic parameter to exclude primitives, <P extends object>
would be the right way to do it... so it isn't pointless.
Note that there is also an Object
interface in TypeScript, starting with an uppercase O
. This interface contains the (apparent) members that exist on everything in JS, like valueOf()
and toString()
. It might be closer to what you were thinking of when you said "everything is an object"; only null
and undefined
are not assignable to Object
. Generally speaking, though, you probably don't want to use the Object
type in TypeScript; such wrapper types are hardly ever what people want to use.
If you really want to capture "anything which can be indexed into like an object", you should probably use the so-called "empty object" type, {}
. This is an object type with no known properties, and behaves like Object
. Again, only null
and undefined
are not assignable to {}
. In fact, it used to be the case that unconstrained generic type parameters (like <P>
instead of <P extends Q>
) were implicitly constrained to {}
. So it used to be quite literally useless to write <P extends {}>
.
Since TypeScript 3.5, however, unconstrained generics are now given an implicit constraint of unknown
instead of {}
. The unknown
type really is "everything" in TypeScript. You can assign any value whatsoever to a variable of type unknown
(but not vice-versa). It is truly pointless to write <P extends unknown>
.
And we might as well end with any
, the ultimate "anything-goes" type. Not only can you assign anything to any
(like unknown
), you can also assign any
to anything (like never
). Using any
is like throwing up your hands and giving up; it's more of a disabling of type checking than it is an actual type. Since TypeScript 3.9, writing <P extends any>
is the same as writing <P extends unknown>
, and therefore, similarly pointless. (It used to be that <P extends any>
allowed you to treat P
like any
when P
was unresolved, like in a generic function implementation, but that was considered silly and changed.)
Playground link to code