TypeScript:对象字面量(大括号对象、type、interface)

介绍

TS 中 {} 代表 对象类型字面量,区别于 JS 普通对象,用来 约束对象的结构:

  • 属性名
  • 属性类型
  • 可选属性
  • 只读属性
  • 方法
  • 索引签名等

区分 JS、TS大括号对象

  • const obj = {} → JS 对象实例
  • type T = {} → TS对象类型约束

应用场景

  • 定义简单实体结构(最常用)。比如:AJAX接口返回数据、表单、简单模型,一次性定义数据结构

  • 函数参数 / 返回值约束

  • 配置对象、选项参数

定义方式1-type定义

使用 type 定义对象结构

  • type:TS 用来 创建自定义类型别名 的语法。
  • User:自定义类型名称(首字母大写,约定俗成)。
  • = 后面大括号 { ... } →【对象字面量类型】

定义对象结构

// 定义类型别名 User
type User = {
  id: number;
  name: string;
  age?: number; // 可选属性
};
解释: id: number;

必填属性:对象必须有 id,值必须是数字,不能省略。

解释:name: string;

必填属性:必须有 name,值为字符串,不能省略。

解释 age?: number;
  • 对象可以写 age,也可以不写 age;
  • 如果写上 age,值必须是 number;
  • 不写 age 等价于 age: number | undefined。

定义对象,有age

const u1: User = {
  id: 1,
  name: "小明",
  age: 18
}

定义对象,没有age(允许,因为age是可选)

const u2: User = {
  id: 2,
  name: "小红"
}

错误定义对象

// 缺少必填 name,报错
const u3: User = { id: 3 }

// id 传字符串,类型不匹配,报错
const u4: User = { id: "4", name: "小李" }

定义方式2-interface

描述一个对象必须具备什么结构。

定义接口

interface User {
  id: number;
  name: string;
  age?: number; // 可选属性
}
解释: id: number;

必填属性:对象必须有 id,值必须是数字,不能省略。

解释:name: string;

必填属性:必须有 name,值为字符串,不能省略。

解释 age?: number;
  • 对象可以写 age,也可以不写 age;
  • 如果写上 age,值必须是 number;
  • 不写 age 等价于 age: number | undefined。

定义对象,有age

const u1: User = {
  id: 1,
  name: "小明",
  age: 18
}

定义对象,没有age(允许,因为age是可选)

const u2: User = {
  id: 2,
  name: "小红"
}

错误定义对象

// 缺少必填 name,报错
const u3: User = { id: 3 }

// id 传字符串,类型不匹配,报错
const u4: User = { id: "4", name: "小李" }

两种定义方式的选择

定义大括号对象,一般情况,type 与 interface 区别不大

优先选择 type(90% 前端业务,接口返回数据、表单)

  • 定义后端返回 DTO、列表数据、普通对象
  • 需要用到联合类型 |、多种状态分支
  • 类型不需要外部扩展、一次性结构
  • 需要组合多个类型(交叉 &)

优先选择 interface

  • 需要声明合并(扩充全局类型、框架类型拓展)
  • 编写类 class implements 实现规范
  • 设计通用对外暴露的公共 API、组件约束

常见用法

1. 可选属性 ?

属性可以不提供,值允许 undefined

type User = {
  id: number;
  email?: string; // 可选
}

const u1: User = { id: 1 };
const u2: User = { id: 2, email: "a@qq.com" };

2. 只读属性 readonly

属性初始化后不能修改,仅约束类型层面

type User = {
  readonly id: number;
  name: string;
}

const u: User = { id: 1, name: "test" };
u.id = 2; // ❌ 报错,只读无法修改
u.name = "new"; // ✅

注意:readonly 只限制属性重新赋值,不限制对象内部可变数据

type Data = {
  readonly list: number[]
}
const d: Data = { list: [1,2] };
d.list.push(3); // ✅ 正常,数组引用没变

3. 对象内定义方法

两种写法等价

type Person = {
  // 写法1:函数属性
  sayHi: () => void;
  // 写法2:方法简写(推荐)
  greet(name: string): string;
}

const p: Person = {
  sayHi() {
    console.log("hi");
  },
  greet(name) {
    return `Hello ${name}`;
  }
}

4. 空对象类型 {}

type EmptyObj = {};

⚠️ 重大误区:
{} 不是“空对象,不能有任何属性”
{} 代表:可以拥有任意属性,但是不能使用 null / undefined

let o: {};
o = 1;     // ✅
o = "abc"; // ✅
o = {};    // ✅
o = null;  // ❌

如果你想要完全空对象,禁止任何属性:

type StrictEmpty = Record<string, never>
const e: StrictEmpty = {};
e.xxx = 1; // ❌ 报错

5. 属性修饰组合

type T = {
  readonly uid: number;
  desc?: string | null;
}

常见坑 & 易错点

坑1:字面量对象多余属性直接赋值报错

type User = { id: number }
const u: User = { id: 1, name: "xx" } // ❌ 对象字面量直接赋值会校验多余属性

绕过方案(不推荐,尽量规范):

const temp = { id:1, name:"xx" }
const u: User = temp; // ✅ 变量中转不再严格校验额外属性

坑2:readonly 仅类型层面,编译后消失

TS 编译成 JS 没有只读限制,运行时依然可以修改。如需运行时保护要用 Object.freeze

坑3:区分 {}、object、Record<string,any>

  1. {}:允许除 null/undefined 以外所有值(原始值、对象都行)
  2. object:只允许引用类型(数组、对象,不能是 number/string 原始值)
  3. Record<string, any>:键为字符串的普通对象
let a: {};
let b: object;
a = 1;   // ✅
b = 1;   // ❌