Object Types in TypeScript

An object type describes the shape of a JavaScript object by listing its properties and their expected types. Learn how to write object types, what the compiler checks, and how they catch common bugs.

6 min read

TypeScript object types describe the shape of a JavaScript object. They list the property names an object must have and what type each property should be. The compiler uses this description to check that you only access properties that exist and that you assign values of the correct type.

In plain JavaScript, accessing a misspelled property returns undefined silently. TypeScript catches that mistake at compile time instead.

Writing an object type

You write an object type between curly braces, listing each property name followed by its type. Separate properties with commas or semicolons -- both work the same way.

typescripttypescript
let user: { name: string; age: number } = {
  name: "Ada",
  age: 30,
};

This tells the compiler that user must have a name of type string and an age of type number. After this declaration, the compiler enforces those rules everywhere user is used.

Here is a function that uses an inline object type for its parameter:

typescripttypescript
function printCoord(pt: { x: number; y: number }) {
  console.log(`x: ${pt.x}, y: ${pt.y}`);
}
 
printCoord({ x: 3, y: 7 }); // x: 3, y: 7

The annotation says that pt must be an object with x and y properties, both numbers. If you try to access a property that does not exist, the compiler stops you before the code ever runs.

The property type can be any TypeScript type: primitives, arrays, unions, other object types, or even function signatures. Nesting is also supported:

typescripttypescript
let config: {
  title: string;
  tags: string[];
  metadata: { created: Date; version: number };
} = {
  title: "My Project",
  tags: ["typescript", "tutorial"],
  metadata: { created: new Date(), version: 1 },
};

The metadata property is itself an object type with its own created and version fields. You can nest as deeply as your data model requires.

What the compiler checks

When you annotate a variable or parameter with an object type, TypeScript checks three things. First, every property you access must be declared in the type. Second, assigned values must match the declared types. Third, the object must have at least the required properties.

Here is an example of a missing property:

typescripttypescript
function greet(person: { name: string; age: number }) {
  console.log(`Hello, ${person.name}.`);
}
 
greet({ name: "Ada" });
// Error: Property 'age' is missing in type '{ name: string; }'
// but required in type '{ name: string; age: number; }'.

TypeScript tells you exactly what is missing and where. This feedback appears in your editor and during compilation, so the problem never reaches runtime.

Excess property checks

There is one extra rule that applies only to object literals: excess property checking. When you pass an object literal directly to a typed variable or function, TypeScript rejects properties not in the type, even if all required properties are present.

typescripttypescript
function createUser(data: { name: string; age: number }) {
  return data;
}
 
// Error: 'email' does not exist in type
createUser({ name: "Ada", age: 30, email: "ada@example.com" });

This catches typos. If you meant to write name but typed naem, the compiler flags it as an unknown property instead of silently creating a new property with undefined as the real name value.

Excess property checking only fires on object literals. If you assign the object to a variable first, the check is skipped because TypeScript assumes you want the extra properties for some other use:

typescripttypescript
const input = { name: "Ada", age: 30, email: "ada@example.com" };
createUser(input); // OK -- variable already has the wider type

The variable input already has a wider inferred type that includes email, so passing it to createUser is allowed. The function only uses name and age, and the extra email does not break anything.

Object types and structural typing

TypeScript uses structural typing: the compiler only cares about the shape of the value, not its name. Two types with the same properties are compatible.

typescripttypescript
interface Point { x: number; y: number; }
interface Coordinate { x: number; y: number; }
 
let p: Point = { x: 10, y: 20 };
let c: Coordinate = p; // OK

This works because both types require the same properties. TypeScript does not check the name, only the structure.

Structural typing also means an object can have more properties than the type requires and still be valid. A function that only needs a label property will accept any object that has at least that property:

typescripttypescript
function printLabel(obj: { label: string }) {
  console.log(obj.label);
}
 
const item = { label: "Milk", price: 2.5, inStock: true };
printLabel(item); // "Milk"

The item variable has three properties, but printLabel only cares about label. This is safe because the function only uses that one property.

When to name your object types

Inline object types are fine for a single function parameter. But when the same shape appears in multiple places, give it a name. Naming the type makes the code easier to read and change. If you add a new required property later, you update one interface instead of three inline annotations.

For more on naming types, see interfaces in TypeScript explained and type aliases in TypeScript explained.

Common mistakes

Expecting object types to exist at runtime. They do not. Object types are erased during compilation. If you need to check the shape of data at runtime -- for example, validating an API response -- you need runtime validation. See runtime validation in TypeScript.

Forgetting that extra properties are usually allowed. Assigning an object with extra properties to a typed variable is fine, as long as it is not an object literal. The excess property check is a special case for literals.

Confusing the type syntax with the value syntax. The curly braces in a type annotation describe a shape. The curly braces in a value create an object. They look similar but serve different purposes. Type annotations only appear after a colon in a variable declaration, parameter, or return type.

Rune AI

Rune AI

Key Insights

  • An object type lists property names and their expected types between curly braces.
  • TypeScript checks that accessed properties exist and that assigned values match the declared types.
  • Object types exist only at compile time and are erased from the compiled JavaScript.
  • Excess property checks catch typos in object literals assigned directly to a typed position.
  • Name reusable shapes with an interface or type alias instead of repeating the inline type.
RunePowered by Rune AI

Frequently Asked Questions

Do object types exist at runtime?

No. Object types are erased during compilation. At runtime, the code is plain JavaScript with no type information. The types only help the compiler check your code before it runs.

Can I use commas instead of semicolons in object types?

Yes. TypeScript allows either comma or semicolon to separate properties, and the last separator is optional. Both `{ x: number; y: number }` and `{ x: number, y: number }` are valid.

What happens if I access a property not in the object type?

The compiler reports an error like `Property 'xyz' does not exist on type '...'`. TypeScript checks that every property you access is declared in the type, which catches typos and incorrect assumptions.

Conclusion

Object types describe the shape of JavaScript objects so the compiler can verify property access, function arguments, and return values. They are the most common type you will write in TypeScript. Write them inline for quick annotations, or name them with an interface or type alias when the same shape is used in multiple places.