TypeScript 对象展开运算符 {...obj} 的类型推断并非简单复制,而是依据源对象是否为精确类型、含索引签名、readonly 或可选属性等结构特征,基于结构兼容性生成新类型:基础字面量展开保持属性完整性与必需性;索引签名保留;readonly 被剥离但可选性保留;联合类型展开后仍为联合而非交叉。

JavaScript 中使用展开运算符({...obj})对对象进行拷贝时,TypeScript 的类型推断行为取决于源对象的类型是否为“精确”(exact)、是否含索引签名、是否有可选属性或 readonly 修饰等。它不是简单地复制类型,而是基于结构兼容性和类型守卫规则生成新类型。
基础对象字面量的展开推断
当源对象是普通对象字面量(无显式类型注解),TypeScript 会推断出一个“鲜活性”(fresh)的字面量类型,包含所有已知属性且为 required:
-
const a = { x: 1, y: 'str' };→ 类型为{ x: number; y: string; } -
const b = { ...a };→ 类型与a完全一致,不丢失属性,也不添加隐式索引签名 - 此时
b和a类型等价,可互赋值
带索引签名的对象展开
若源对象类型含字符串索引签名(如 { [key: string]: any }),展开后仍保留该签名;但若原类型是更严格的映射(如 { [K in keyof T]: T[K] }),展开可能触发分布条件类型简化:
-
type Obj = { [k: string]: number }; const o: Obj = { a: 1 }; const c = { ...o };→c类型仍为Obj - 但若
o是泛型类型参数(如函数内<t extends record unknown>>(t: T) => { ...t }</t>),展开结果类型会保留T的约束,而非宽化为Record
readonly、optional 与展开的关系
展开运算符会剥离 readonly 修饰,但保留可选性(?):
使用 Scrapfly SDK(scrapfly-sdk)在 TypeScript 或 JavaScript 中进行网页抓取,用于用户请求抓取 URL、绕过反爬虫防护、渲染页面等场景。
立即学习“Java免费学习笔记(深入)”;
-
const r = { readonly x: 1 } as const;→ 展开后x变为可写(非 readonly) -
type Opt = { y?: string }; const o: Opt = {}; const d = { ...o };→d类型仍是Opt,y保持可选 - 注意:如果原始对象字面量本身没提供可选属性值(如
{}),展开后 TypeScript 可能推断为更宽松的类型(如{}→{},而非保留Opt),除非显式标注
联合类型与展开的保守推断
当源对象是联合类型(如 A | B),展开结果类型是各分支分别展开后的联合:
type A = { a: number }; type B = { b: string }; const u: A | B = Math.random() > 0.5 ? { a: 1 } : { b: 'x' };-
const v = { ...u };→ 类型为A | B,不是交叉或宽化类型 - TypeScript 不会尝试合并属性(即不会变成
{ a?: number; b?: string }),除非你用Partial或映射类型主动转换
不复杂但容易忽略。关键在于:展开不改变结构,只“解构再重组”,类型推断始终以源类型的可分配性为基础,而非运行时行为。

















