TypeScript: TypeScript Object Types

Last updated: 2026-08-26

In JavaScript, objects are the most commonly used data structures. TypeScript’s object type allows you to precisely describe “what properties an object should have and what their types are”—this is at the core of its structured type system.

1. Object Type Basics

(1) Anonymous Object Types

The most straightforward approach—specifying the object's structure directly at the type declaration:

TYPESCRIPT
let user: { name: string; age: number } = {
  name: "Charlie",
  age: 20
};

(2) Type Inference

When an object literal is assigned to a variable, TypeScript automatically infers the type:

TYPESCRIPT
let user = {
  name: "Charlie",
  age: 20
};
// Inferred as { name: string; age: number }

user.name = "Diana";   // ✅
user.age = 21;        // ✅
// user.email = "x@x.com";  // ❌ Not included in the types of inferences email Properties

(3) Multi-line Syntax

When an object has many properties, it is recommended to use a multi-line format (separated by semicolons or commas):

TYPESCRIPT
let product: {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
} = {
  id: 1,
  name: "TypeScriptAuthoritative Guide",
  price: 89.9,
  inStock: true
};

▶ Example: Object Types for Function Parameters

TYPESCRIPT
function createUser(user: { name: string; age: number; email: string }): string {
  return `${user.name},${user.age} years old,Email:${user.email}`;
}

console.log(createUser({
  name: "Charlie",
  age: 20,
  email: "xiaoming@example.com"
}));
▶ Try it Yourself

Output:

TEXT 📖 Display only
Charlie,20 years old,Email:xiaoming@example.com


2. Optional Properties

Use ? to mark an optional attribute—this attribute may be omitted:

(1) Syntax

TYPESCRIPT
let user: { name: string; age?: number } = {
  name: "Charlie"
  // age This information is optional.
};

console.log(user.name);  // "Charlie"
console.log(user.age);   // undefined

(2) Types of Optional Attributes

The types of optional attributes automatically include undefined:

TYPESCRIPT
// age?: number Equivalent to age: number | undefined
// But there is an important difference:age?: number The property does not exist.,age: number | undefined The property must exist.

let config: { host: string; port?: number } = { host: "localhost" };
// config.port is undefined(The property does not exist either. ok 's)

let config2: { host: string; port: number | undefined } = { host: "localhost" };
// ❌ Error! port property must exist,Even if undefined You must also explicitly write
// Correct Way to Write It:{ host: "localhost", port: undefined }

(3) Secure Methods for Accessing Optional Attributes

TYPESCRIPT
interface User {
  name: string;
  age?: number;
}

function getAgeText(user: User): string {
  // ❌ Unsafe——If age Does not exist,undefined.toFixed() It crashes during runtime
  // return user.age.toFixed(0) + " years old";

  // ✅ Method 1:Check first
  if (user.age !== undefined) {
    return user.age.toFixed(0) + " years old";
  }
  return "Age unknown";

  // ✅ Method 2:Provide a default value
  // return (user.age ?? 0).toFixed(0) + " years old";
}


3. Read-Only Properties

Use the readonly tag to mark a property as read-only—it cannot be modified after assignment:

TYPESCRIPT
let user: { readonly id: number; name: string } = {
  id: 1,
  name: "Charlie"
};

user.name = "Diana";     // ✅ General attributes can be modified
// user.id = 2;         // ❌ Read-only properties cannot be modified.

(2) readonly is a shallow read-only modifier

TYPESCRIPT
let config: { readonly data: { value: number } } = {
  data: { value: 42 }
};

// config.data = { value: 100 };  // ❌ data Cannot be reassigned
config.data.value = 100;          // ✅ data The internal properties can still be modified!
💡 Deep Read-Only: If you need all levels of an object to be read-only, use the Readonly<type> tool type (nested) or the as const assertion. Lesson 19 will cover the Readonly, Partial, Required, and other tool types.



4. Index Signatures

When the property name of an object is unknown but the type of the property value is known, use an index signature:

(1) String Index Signature

TYPESCRIPT
let scores: { [key: string]: number } = {
  Chinese Language: 90,
  Mathematics: 95,
  English: 88
};

console.log(scores["Mathematics"]);   // 95

// Add a New Property——As long as it is string key + number Any value will do.
scores["Physics"] = 85;   // ✅

(2) Digital Index Signatures

TYPESCRIPT
let arr: { [index: number]: string } = {
  0: "first",
  1: "second",
  2: "third"
};

console.log(arr[1]);  // "second"

(3) Limitations on Index Signatures

The index signature requires that the types of all known attributes must be subtypes of the index value type:

TYPESCRIPT
// ❌ Error: name is string,However, index signatures require that all values be number
// let config: { [key: string]: number; name: string } = { name: "test" };

// ✅ Correct:All value types are consistent
let config: { [key: string]: string; name: string } = { name: "test" };

(4) Template literal index signatures (TypeScript 4.4+)

TYPESCRIPT
let css: { [key: `--${string}`]: string } = {
  "--primary-color": "#4A90D9",
  "--font-size": "14px"
};

▶ Example: Implementing Dynamic Configuration Using Index Signatures

TYPESCRIPT
interface EnvConfig {
  [key: string]: string | number | boolean;
  // All values can be string、number or boolean
  // Known properties must also be compatible.
  NODE_ENV: string;
  PORT: number;
}

let env: EnvConfig = {
  NODE_ENV: "development",
  PORT: 3000,
  DEBUG: true,        // ✅ Dynamic Properties
  API_URL: "http://localhost:8080"  // ✅ Dynamic Properties
};

console.log(`Environment:${env.NODE_ENV}`);
console.log(`Port:${env.PORT}`);
console.log(`Debugging:${env.DEBUG}`);
▶ Try it Yourself

Output:

TEXT 📖 Display only
Environment:development
Port:3000
Debugging:true


5. Nested Object Types

In actual development, objects are often nested in multiple levels—TypeScript supports describing types layer by layer:

(1) Types of nested object literals

TYPESCRIPT
let user: {
  name: string;
  address: {
    city: string;
    zip: string;
  };
  tags: string[];
} = {
  name: "Charlie",
  address: {
    city: "Beijing",
    zip: "100000"
  },
  tags: ["Developer", "TypeScript"]
};

console.log(user.address.city);  // "Beijing"
console.log(user.tags[0]);       // "Developer"

(2) Multi-level nesting

TYPESCRIPT
type ApiResponse = {
  status: number;
  data: {
    user: {
      id: number;
      profile: {
        avatar: string;
        bio: string;
      };
    };
  };
};

let response: ApiResponse = {
  status: 200,
  data: {
    user: {
      id: 1,
      profile: {
        avatar: "https://example.com/avatar.png",
        bio: "TypeScript Enthusiast"
      }
    }
  }
};

console.log(response.data.user.profile.bio);  // "TypeScript Enthusiast"
💡 Tip: When nesting exceeds 2–3 levels, you should extract the inner types into separate interfaces or type aliases to improve readability and reusability. This will be covered in detail in Lessons 9–11.



6. Two Styles of Object Types

(1) Anonymous Types (Inline)

TYPESCRIPT
function greet(user: { name: string; age: number }): string {
  return `Hello,${user.name}!`;
}

Suitable for: simple types used only once, function parameters

(2) Naming Types (interface / type)

TYPESCRIPT
interface User {
  name: string;
  age: number;
}

function greet(user: User): string {
  return `Hello,${user.name}!`;
}

Suitable for: Multiple uses, numerous properties, and situations requiring inheritance or extension

📌 Recommendation: If there are more than three properties or if you need to reuse a type, extract it into an interface or a type alias. For simple one-time parameters, use an anonymous type.



7. Structured Type Systems

TypeScript uses a structured type system—as long as an object’s structure meets the type requirements, it is compatible, regardless of whether the type names match:

(1) Structural Matching

TYPESCRIPT
interface Point {
  x: number;
  y: number;
}

let point: Point = { x: 1, y: 2 };   // ✅ Has x and y

let point3D = { x: 1, y: 2, z: 3 };
point = point3D;   // ✅ point3D has x and y,Redundant attributes do not conflict

(2) Check for Unnecessary Attributes

When assigning a value directly to an object literal, TypeScript performs a "redundant property check"—it does not allow properties that do not exist in the target type:

TYPESCRIPT
interface Point {
  x: number;
  y: number;
}

// ❌ Check for Unnecessary Attributes——No additional properties are allowed when directly assigning an object literal.
// let p: Point = { x: 1, y: 2, z: 3 };

// ✅ Method 1:Bypassing via Variables(Because variables do not trigger unnecessary property checks)
let temp = { x: 1, y: 2, z: 3 };
let p: Point = temp;   // ✅

// ✅ Method 2:Using Type Assertions
let p2: Point = { x: 1, y: 2, z: 3 } as Point;

// ✅ Method 3:Extension Interface(Top Recommendations)
interface Point3D extends Point {
  z: number;
}
let p3: Point3D = { x: 1, y: 2, z: 3 };
💡 Why check for extra properties? This is to catch spelling mistakes. If you write { x: 1, y: 2, zzz: 3 }, TypeScript will immediately alert you—zzz isn’t a property of Point; did you misspell it?

▶ Example: Structured Types in Practice

TYPESCRIPT
interface Printable {
  toString(): string;
}

// Date has toString method,Structural Matching
let date: Printable = new Date();

// Custom objects include toString Methods,Also matches
let custom: Printable = {
  toString() { return "Custom Objects"; }
};

function print(obj: Printable): void {
  console.log(obj.toString());
}

print(date);    // Output Date the string representation of
print(custom);  // Output "Custom Objects"
▶ Try it Yourself

Output:

TEXT 📖 Display only
<current date ISO string>
Custom Objects

❓ FAQ

Q Should object types use interface or type aliases?
A In most cases, the two are equivalent. interface is better suited for defining object shapes (supporting declaration merging and extends inheritance), while type is better suited for complex operations such as union types and conditional types. As long as your team uses a consistent approach, it’s fine. A detailed comparison will follow later in this chapter. Beginners are recommended to start with interface.
Q What is the difference between optional properties and the undefined union type?
A age?: number allows properties to be absent (completely missing), while age: number | undefined requires that properties must exist but their values can be undefined. In actual development, optional properties are more commonly used—omitting a property feels more natural than explicitly writing undefined.
Q Why do object literals undergo extra property checks, but variables do not?
A When assigning values directly to object literals, developers usually know exactly what they’ve written—extra properties are most likely typos. When assigning values to variables, the variable’s type may come from elsewhere, and extra properties might be valid (such as additional properties from a subtype). This is TypeScript’s trade-off between “safety” and “flexibility.”
Q Are values with the readonly attribute truly immutable?
A readonly is only a compile-time check; there is no runtime protection. It can be bypassed using type assertions or the any type. However, it effectively prevents "accidental modifications"—which is the primary purpose of the type system: to catch errors at compile time, rather than providing runtime protection.

📖 Summary

📝 Exercises

  1. Basic Problem (Difficulty ⭐): Define an object type to represent a "book" (title, author, price, inStock?), create two book objects—one with inStock and one without—and print their titles and prices.
  2. Advanced Problem (Difficulty ⭐⭐): Write a function updateUser(user: { name: string; age: number; email?: string }, updates: { age?: number; email?: string }) that returns the updated user object. The original user's name must be immutable (use readonly).
  3. Challenge (Difficulty ⭐⭐⭐): Define a type CacheStore using index signatures, where the key is a string and the value is { data: T; timestamp: number } (T is a generic type). Create an instance of CacheStore<string>, store two cache entries in it, and then iterate through and output all cached keys and their expiration statuses (assuming a 5-second expiration).
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏