Open LukeAbby opened 1 week ago
Similar footgun types include, but are not limited to:
{}
; this is not an empty object, it instead includes numbers, strings, arrays, literally everything but null
and undefined
. Record<string, any>
; this includes plain objects, arrays, and functions. This probably is meant to be Record<string, unknown>
which is only plain objects.Record<string | number, T>
this doesn't include arrays. Use Record<string, T> | Record<number, T>
for that.Record<any, T>
this only includes objects.Record<never, T>
- this probably was supposed to be Record<string, never>
which is the closest type to an object with no properties. Record<never, T>
allows any object.Record<string, unknown>
- despite only being for plain objects, this has the footgun that interfaces can't be assigned to it. This is somewhat illogical if they already have properties. This can be rectified on the user's end by merging in the index signature. This also has the second footgun of not allowing readonly properties.Therefore the best constraint to use for most objects would be { readonly [K: string]: T }
unless mutability is actively desired.
object
is the wrong type in 99% of cases because it includes all functions and all arrays. It has some limited usage in complex scenarios, mostly to prevent an index signature but that only matters when the base constraint is used or in other similarly niche scenarios.Even when any object, any array, or any function is truly meant it's more self documenting to say
Record<string, unknown> | Record<number, unknown> | (...args: any[]) => any
. If this is a common situation then helper types could shorten it.