为何使用展开语法向组件传props时TypeScript未检出多余属性错误?
为什么通过展开语句向组件传递props时,TypeScript的类型校验会失效?
问题复现
以下方代码为例,我们给Props仅定义了someTitle字段的组件,通过展开语法传入了额外的TICK属性,但TS和linter都没有抛出类型错误:
type SomeComponentProps = Readonly<{ someTitle: string }> const SomeComponent = ({ someTitle }: SomeComponentProps) => { return <>someTitle</> } const MainComponent = () => { const someTitle = 'Some title' const TICK = 'TICK' return <SomeComponent {...{ someTitle, TICK }} /> }
核心原因
你期待的「传入未定义的Props就报错」的能力,来自TS的多余属性检查(Excess Property Checks),这是一个特殊的语法校验规则,不属于TS结构化类型系统的默认逻辑,它只会在非常窄的场景下触发:
- 只有当你直接把对象字面量赋值给明确标注类型的变量,或者直接把对象字面量作为参数传给明确标注类型的形参时,TS才会检查对象里有没有多余的、目标类型没定义的字段,存在就报错。
而JSX里的展开传参,刚好绕开了这个检查的触发条件:
- 你写的
{...{ someTitle, TICK }}里,内层的对象字面量没有被直接标注为SomeComponentProps类型,TS会先把它推导成一个独立的匿名类型:{ someTitle: string; TICK: string } - 接下来TS只会做通用的结构化类型兼容性校验:检查这个匿名类型是不是满足
SomeComponentProps的所有要求——也就是有没有类型为string的someTitle字段。检查通过就判定类型合法,不会额外管多出来的TICK字段。 - 结构化类型系统本身不禁止源类型比目标类型多字段,只要目标类型要求的字段都存在、类型匹配,就认为类型兼容。
怎么让展开传参也触发多余属性校验
只要把要展开的对象提前显式标注为组件的Props类型,多余属性检查就会在对象定义的位置触发:
const MainComponent = () => { const someTitle = 'Some title' const TICK = 'TICK' // 这里显式标注类型,写TICK字段时TS会直接报错:对象字面量中存在多余属性TICK const componentProps: SomeComponentProps = { someTitle, TICK } return <SomeComponent {...componentProps} /> }
作为对比,如果你不通过展开,直接在JSX标签上写属性(比如<SomeComponent someTitle="xx" TICK="TICK" />),TS会直接报错,因为这种写法刚好符合多余属性检查的触发条件,相当于直接把属性组成的对象字面量绑定到了Props类型的位置。
内容的提问来源于stack exchange,提问作者Timur Zakirov
相关产品推荐
相关产品推荐

