skip to content

In TypeScript, what values are assignable to `object`, to `{}` and to `Object`, and which of the three actually means "any non-primitive value"?

level: middleimportance: should knowfreq 45%

answer

  1. only one of the three excludes primitives
  2. {} does not mean "empty object"
  3. think "non-nullish", not "object"
  4. Object is an interface almost everything satisfies
  5. unknown or Record is usually the real answer

basics

~20 s

Only lowercase object means "non-primitive": it accepts arrays, functions and class instances and rejects every primitive. The type {} means "anything except null and undefined", so strings and numbers satisfy it, and Object behaves essentially the same way.

solid answer

~40 s

They sound like three spellings of one idea and behave quite differently. Lowercase `object` is the only one that means "not a primitive" — it accepts arrays, functions, class instances and object literals, and rejects `string`, `number`, `boolean`, `bigint`, `symbol`, `null` and `undefined`. `{}` means "a value with no required members", which under `strictNullChecks` is everything except `null` and `undefined` — `const x: {} = "hi"` compiles, which surprises people who read it as "empty object". `Object` is the standard-library interface declaring `toString`, `valueOf`, `hasOwnProperty` and friends; primitives satisfy those too, so it is nearly interchangeable with `{}` and is likewise not a way to say "an object". In practice: use `object` for a non-primitive, `Record<string, unknown>` when you intend to index it, and `unknown` when you really mean any value at all.

code

typescript · 10 lines
typescript
const arr: object = [1, 2];   // ok
const fn: object = () => {};  // ok
// @ts-expect-error string is not assignable to object
const str: object = "hi";

const anyNonNullish: {} = "hi"; // ok — {} accepts primitives
// @ts-expect-error null is not assignable to {}
const nothing: {} = null;

const viaInterface: Object = 42; // ok — primitives satisfy Object too

go deeper

for a junior

Remember that lowercase object is the one that rejects primitives, and that {} is far more permissive than it looks. Knowing to reach for unknown or a concrete shape instead already covers most cases.

for a middle

Explain each one by what it accepts: object excludes primitives, {} accepts anything non-nullish because it requires no members, and Object is an interface primitives also satisfy. Be able to say why the error surfaces at the use site rather than the call.

for a senior

Show the judgment call — when you genuinely need object versus Record<string, unknown> versus unknown, and why a vague annotation here usually means an unpinned shape. Explain that none of them performs a runtime check, so a real guard is still required.

for a principal

Treat these types as a codebase-hygiene lever: a spread of {} and Object annotations is a signal that boundaries were never modelled. Decide the house convention for untyped inbound data and where validation converts unknown into a real shape.

## Three types that read alike Ask what each one accepts and the differences become sharp. ### `object` — the non-primitive type Lowercase `object` is a built-in type meaning "anything that is not a primitive". Arrays, functions, class instances, plain object literals: yes. `string`, `number`, `boolean`, `bigint`, `symbol`, `null`, `undefined`: no. ```ts const a: object = [1, 2]; // ok const b: object = () => {}; // ok — functions are objects const c: object = new Date(); // ok const d: object = "hi"; // error: 'string' is not assignable to 'object' ``` Note what it does *not* give you: `object` declares no members, so you cannot index into it or read a property off it without narrowing first. It answers "is this a primitive?" and nothing else. ### `{}` — anything but null and undefined `{}` is an object type with no required members. Structural typing then says: any value that has *at least* nothing qualifies. Under `strictNullChecks`, that is every value except `null` and `undefined` — including primitives. ```ts const e: {} = "hi"; // ok — surprising, but a string has "at least nothing" const f: {} = 42; // ok const g: {} = null; // error under strictNullChecks ``` So `{}` is not "an empty object", and it is not even "an object". Read it as **non-nullish**. That reading is exactly why it turns up deliberately in intersections: intersecting a type with `{}` strips `null` and `undefined` out of it, which is how the standard library expresses non-nullability. One genuine restriction survives: because the declared type has no members, you cannot read anything off a `{}`-typed value without narrowing or asserting first. ### `Object` — the wrapper-side interface Capital `Object` is an interface in the standard library declaring the members every value inherits through the prototype chain — `toString`, `valueOf`, `hasOwnProperty`, `isPrototypeOf` and so on. Primitives structurally satisfy all of them, so `Object` accepts strings and numbers too, and behaves so much like `{}` that the distinction rarely matters in practice. What matters is the shared conclusion: **neither `{}` nor `Object` restricts a value to being an object.** ## Why people reach for these and what they should write instead The usual intent behind writing one of the three is one of four things: - **"Any value at all."** Use `unknown`. It accepts everything including `null` and `undefined`, and forces a narrowing step before use, which is what you wanted. - **"Not a primitive."** Use `object`. This is the one case where `object` is genuinely the right answer — for example a helper that must reject primitives before walking a structure. - **"A dictionary I will read keys from."** Use `Record<string, unknown>`. `object` refuses indexing; a record type describes the shape you actually plan to use. - **"Some specific shape."** Write the interface or type alias. This is by far the most common real answer, and reaching for one of the three vague types is usually a sign that the shape was never pinned down. ## An error message that confuses everyone Because `{}` accepts primitives, a mistake with it does not fail where you expect. Assigning is fine; the error arrives later, when you try to use the value: ```ts function show(v: {}) { return v.name; // error: property 'name' does not exist on type '{}' } show("hello"); // the call itself is perfectly legal ``` Candidates who believe `{}` means "an object" expect the call site to be the error and spend time in the wrong place. ## Erasure, as always None of the three emits anything or performs a runtime check. `object` does not test `typeof v === "object"` for you; `{}` does not verify anything is present. They constrain what the checker permits, and the compiled JavaScript is identical with or without them. Any actual "is this an object?" test has to be written as a real runtime check that then narrows the type. ## The short version `object` = not a primitive. `{}` = not null or undefined (primitives welcome). `Object` = the interface almost everything satisfies, so effectively the same as `{}`. When you want "any value", say `unknown`; when you want a dictionary, say `Record<string, unknown>`; when you know the shape, write the shape.

  • Why does `const x: {} = "hello"` compile when the annotation looks like an empty object?
    `{}` is an object type with no required members, and TypeScript is structural: a value is assignable when it has everything the target requires, and this target requires nothing. A string clears that bar. Under `strictNullChecks` only `null` and `undefined` fail, so the right mental reading is "any non-nullish value", not "an empty object".
  • If a helper must accept only non-primitives, what do you annotate and what do you still have to do inside?
    Annotate `object` — it is the one of the three that rejects strings, numbers, booleans, bigints, symbols, null and undefined. Inside, `object` declares no members, so you still cannot read or index a property without narrowing. Either narrow with a runtime check, or take `Record<string, unknown>` instead if indexing is the point.
  • Someone proposes `Object` as the parameter type for "any object". What is wrong with it?
    It does not mean what they think. `Object` is the standard-library interface for the members every value inherits — `toString`, `valueOf`, `hasOwnProperty` — and primitives satisfy all of them, so strings and numbers are accepted. It behaves essentially like `{}`. If the intent is "not a primitive", the answer is lowercase `object`.

saying these in an interview costs you the question

  • Reads {} as "an object with no properties".
  • Thinks Object is the safe general-purpose object type.
  • Believes object allows property access without narrowing.
  • Assumes all three reject primitives.
  • Uses {} where unknown was meant.

context