To check if an object is empty in TypeScript, count its own keys: Object.keys(obj).length === 0 is true for {} and false as soon as the object has a property. In real code you also need to handle null and undefined, and decide what to do with arrays, dates and class instances. This guide builds a type-safe isEmptyObject check, compares Object.keys, JSON.stringify, for...in and Reflect.ownKeys, and explains the TypeScript empty object type (why {} is not what you think).
Every example was compiled with TypeScript 5.2.2 and Node.js v22.22.2 in the Windows Command Prompt, using tsc --target es2022 --lib es2023,dom --strict file.ts and node file.js. Reference: Object.keys() on MDN and Record<Keys, Type> in the TypeScript Handbook.
Check if an object is empty with Object.keys()
const settings: Record<string, unknown> = {};
const user = { name: "Ava", city: "Chicago" };
console.log(Object.keys(settings).length === 0); // true -> empty
console.log(Object.keys(user).length === 0); // false -> has properties
settings.theme = "dark";
console.log(Object.keys(settings).length === 0); // false now
Output:
true
false
false
Object.keys(obj).length === 0 before and after adding a property.Object.keys() returns the object’s own enumerable string keys. A property whose value is undefined still counts as a key, so { a: undefined } is not empty.
A reusable, type-safe isEmptyObject function
This helper accepts any value, so you can call it on API data of unknown shape. It returns false for null, undefined, arrays, dates and maps, and its return type value is Record<string, never> is a type guard that narrows the value when the result is true:
function isEmptyObject(value: unknown): value is Record<string, never> {
return (
typeof value === "object" &&
value !== null &&
Object.getPrototypeOf(value) === Object.prototype && // only plain {} objects
Object.keys(value).length === 0
);
}
const samples: [string, unknown][] = [
["{}", {}],
["{ a: 1 }", { a: 1 }],
["{ a: undefined }", { a: undefined }],
["null", null],
["undefined", undefined],
["[]", []],
["new Date()", new Date()],
["new Map()", new Map()],
["Object.create(null)", Object.create(null)],
];
for (const [label, value] of samples) {
console.log(label.padEnd(20), isEmptyObject(value));
}
Output:
{} true
{ a: 1 } false
{ a: undefined } false
null false
undefined false
[] false
new Date() false
new Map() false
Object.create(null) false
Object.create(null) has no prototype, so the prototype check rejects it. If you work with such dictionaries, change that line to also accept null as the prototype. new Date() and new Map() would pass a plain Object.keys test (they have no own keys), which is why the helper checks the prototype. For a Map or Set, use .size === 0 instead.
Handle null and undefined
Object.keys(null) throws a TypeError. If a value can be missing, fall back to an empty object with ??:
function hasNoKeys(obj: object | null | undefined): boolean {
return Object.keys(obj ?? {}).length === 0; // ?? {} treats null/undefined as empty
}
const apiResponse = JSON.parse("null") as Record<string, unknown> | null;
console.log(hasNoKeys(apiResponse));
console.log(hasNoKeys({ id: 7 }));
try {
Object.keys(apiResponse as object); // what happens without the check
} catch (e) {
console.log(String(e));
}
Output:
true
false
TypeError: Cannot convert undefined or null to object
Object.keys vs JSON.stringify vs for…in
Every common empty-object check gives the same answer for normal data objects, but they disagree on the edge cases:
const sym = Symbol("id");
const cases: Record<string, object> = {
"plain {}": {},
"symbol key": { [sym]: 1 },
"non-enumerable": Object.defineProperty({}, "hidden", { value: 1, enumerable: false }),
"inherited only": Object.create({ inherited: 1 }),
"new Date()": new Date(),
};
const isEmpty = {
keys: (o: object) => Object.keys(o).length === 0,
json: (o: object) => JSON.stringify(o) === "{}",
forIn: (o: object) => { for (const _ in o) return false; return true; },
ownKeys: (o: object) => Reflect.ownKeys(o).length === 0,
};
console.log("case".padEnd(16), Object.keys(isEmpty).map(k => k.padEnd(8)).join(""));
for (const [label, obj] of Object.entries(cases)) {
console.log(label.padEnd(16), Object.values(isEmpty).map(f => String(f(obj)).padEnd(8)).join(""));
}
Output:
case keys json forIn ownKeys
plain {} true true true true
symbol key true true true false
non-enumerable true true true false
inherited only true true false true
new Date() true false true true
| Check | What it counts | Notes |
|---|---|---|
Object.keys(o).length === 0 | Own enumerable string keys | Fast and clear; the usual choice |
JSON.stringify(o) === "{}" | What JSON would include | Slow on large objects; a Date becomes a string, so it is not “{}” |
for...in loop | Own and inherited enumerable keys | Stops at the first key, no array created |
Reflect.ownKeys(o).length === 0 | All own keys, including symbols and non-enumerable | The strictest check |
Lodash’s _.isEmpty() works too, but it also returns true for numbers, booleans and empty strings, so it is less precise than a dedicated object check.
The TypeScript empty object type
A common mistake is typing a value as {} to mean “an empty object”. In TypeScript, {} means any value that is not null or undefined, so even a number is allowed:
let anything: {} = 42; // compiles: {} means "any value except null and undefined"
anything = "text";
console.log(typeof anything);
type EmptyObject = Record<string, never>;
const empty: EmptyObject = {}; // only an object with no properties
console.log(Object.keys(empty).length);
Output:
string
0
To type an object that must have no properties, use Record<string, never>. Adding a property is then a compile-time error:
type EmptyObject = Record<string, never>;
const ok: EmptyObject = {};
const notEmpty: EmptyObject = { page: 1 };
tsc output:
empty_type_error.ts(4,33): error TS2322: Type 'number' is not assignable to type 'never'.
Record<string, never> rejects any property.For “any object” (not a primitive), use object or Record<string, unknown>.
Treat objects with only empty values as empty
Form filters and query parameters often have keys whose values are blank. Remove those first, then check the keys:
interface Filters {
city?: string;
minPrice?: number;
tags?: string[];
}
const filters: Filters = { city: "", minPrice: undefined, tags: [] };
const active = Object.fromEntries(
Object.entries(filters).filter(([, v]) => v !== undefined && v !== "" && !(Array.isArray(v) && v.length === 0))
) as Partial<Filters>;
console.log(active, Object.keys(active).length === 0 ? "-> no filters set" : "-> filters set");
Output:
{} -> no filters set
More TypeScript tutorials you may find useful:
- Check if a string is empty in TypeScript
- Check if an array is null or empty
- Remove a property from an object in TypeScript
- Check the type of a variable in TypeScript
Frequently asked questions
How do I check if an object is empty in TypeScript?
Use Object.keys(obj).length === 0. Add obj != null first if the object can be null or undefined.
What is the TypeScript type for an empty object?
Record<string, never>. The type {} means any non-null value, not an empty object.
Is { a: undefined } an empty object?
No. It has the key a, so Object.keys() returns ["a"]. Remove undefined values first if they should not count.
Why does Object.keys(new Date()) return an empty array?
A Date has no own enumerable properties. Check that the prototype is Object.prototype if only plain objects should count as empty.
Can I use JSON.stringify(obj) === “{}”?
It works for plain data objects, but it is slower and gives surprising results for dates, symbols and undefined values.
How do I check if a Map or Set is empty?
Use the size property: map.size === 0.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn