TypeScript设置属性可选后赋值空对象报类型不匹配错误
问题根源
你的条件类型仅对函数入参props做了类型校验,没有在函数内部收窄泛型T的实际范围:
- 泛型
T的约束始终是extends ObjectLiteral,TS认为它可以是任意满足该约束的子类型,比如你显式传入T = { id: number }时,state必须包含id属性,空对象{}显然不符合这个子类型的要求。 - 表达式
props.state || {}的推导类型是T | {},而你声明的my_props.state要求类型为T,TS不允许将更宽泛的类型赋值给可能更具体的子类型T,因此抛出类型错误。 - 你写的
IfEquals判断只在入参校验阶段生效,不会让TS在函数内部自动推断「当state缺失时T一定等于ObjectLiteral」这个逻辑。
修复方案
两种常用写法都可以解决问题,且不破坏你原本的入参校验逻辑:
方案1:安全类型断言(改动最小)
在明确逻辑正确性的前提下,给默认值加类型断言,同时把||替换为??避免state为合法falsy值(比如0、空字符串)时被错误替换为空对象:
type ObjectLiteral = Record<string, any>; // 补全代码中缺失的工具类型定义 type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? A : B; type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>; type FuncProps<T> = {state: T}; const test = <T extends ObjectLiteral = ObjectLiteral>(props: IfEquals<T, ObjectLiteral, "same", "diff"> extends "same" ? Optional<FuncProps<T>, "state"> : FuncProps<T>) => { const my_props: FuncProps<T> = { ...props, // 仅当T为默认ObjectLiteral时state可能缺失,此时{}完全符合类型要求 state: (props.state ?? {}) as T }; return my_props; } // 入参校验逻辑完全符合预期 test<{address: {status: boolean}}>({ state: {address: {status: true}}}); test<Record<string, any>>({}); test({});
方案2:函数重载拆分场景(无类型断言)
通过TS函数重载把两种泛型场景的类型完全拆分,内部实现用宽松类型编写,不需要额外断言:
type ObjectLiteral = Record<string, any>; type FuncProps<T> = {state: T}; type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>; // 重载1:使用默认泛型时,state可选 function test(): FuncProps<ObjectLiteral>; // 重载2:传入自定义泛型时,state必填 function test<T extends ObjectLiteral>(props: FuncProps<T>): FuncProps<T>; // 内部实现签名不对外暴露,使用宽松类型即可 function test(props: FuncProps<any> = {state: {}}) { return { ...props, state: props.state ?? {} }; } // 外部调用校验完全符合预期 test<{address: {status: boolean}}>({ state: {address: {status: true}}}); test<Record<string, any>>({}); test({});
内容的提问来源于stack exchange,提问作者Radagast
相关产品推荐
相关产品推荐

