将对象字面量赋值给TypeScript类型时如何保留类型信息?
问题描述
定义如下类型:
type Foo = { foo?: number[] bar?: number[] }
当直接用包含foo的字面量赋值给Foo类型变量时,TypeScript会丢失foo已被赋值的类型信息,导致调用push时报错:
const foo1: Foo = { foo: [] } foo1.foo.push(3) // Error: foo1.foo is possibly undefined
但如果先赋值空对象再单独给foo赋值,TypeScript却能正确追踪到foo已存在:
const foo2: Foo = {} foo2.foo = [] foo2.foo.push(3) // OK
使用satisfies操作符虽然能保留foo的存在性,但会失去对其他可选属性(比如bar)的赋值能力:
const foo3 = { foo: [] } satisfies Foo foo3.foo.push(3) // OK foo3.bar = [] // Error: Property 'bar' does not exist on type '{ foo: never[]; }'
如何让TypeScript既识别字面量中已赋值的可选属性,又保留对其他可选属性的赋值能力?
解决方案
方案1:交叉原类型与字面量类型
先创建字面量变量,再将其赋值为原类型与字面量类型的交叉类型,既能保证符合原类型约束,又能保留已赋值属性的非可选性:
const temp = { foo: [] }; const foo1: Foo & typeof temp = temp; foo1.foo.push(3); // OK foo1.bar = []; // OK
方案2:自定义工具类型标记已赋值属性
创建工具类型,将指定的可选属性转为必选,同时保留原类型的其他属性:
type WithDefined<T, K extends keyof T> = T & { [P in K]-?: T[P] }; const foo1: WithDefined<Foo, 'foo'> = { foo: [] }; foo1.foo.push(3); // OK foo1.bar = []; // OK
方案3:类型断言结合交叉类型
直接通过类型断言将字面量转为原类型与已赋值属性的交叉类型,无需额外临时变量:
const foo1 = { foo: [] } as Foo & { foo: number[] }; foo1.foo.push(3); // OK foo1.bar = []; // OK
原理说明
上述方案的核心是让变量的类型同时包含原类型的约束和字面量中已赋值属性的具体信息:
- 原类型
Foo保证变量可以拥有所有可选属性(包括未在字面量中赋值的bar) - 交叉的字面量类型或工具类型则明确标记了已赋值的
foo属性是必存在的,消除了undefined的可能性
内容的提问来源于stack exchange,提问作者riv
相关产品推荐
相关产品推荐

