Spread operator in TypeScript is not sound!!
When I was building MigaCSS, I met a bug that I wondered why TypeScript couldn’t catch it.
Here is some code illustrating the issue.
tsx
importReact from 'react'functionA () {constprops = {onClick : () => {},jser : 'dev'}return <B {...props }/>}functionB (props : {onClick :() => void}) {return <div {...props }/>}
tsx
importReact from 'react'functionA () {constprops = {onClick : () => {},jser : 'dev'}return <B {...props }/>}functionB (props : {onClick :() => void}) {return <div {...props }/>}
I accidentally passed down props to child components, "jser"
is not on B
but
TypeScript doesn’t complain.
I’d expect it to be complained because it does if not with spread.
tsx
importReact from 'react'functionA () {constprops = {onClick : () => {},jser : 'dev'}return <Type '{ onClick: () => void; jser: string; }' is not assignable to type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'. Property 'jser' does not exist on type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'.2322Type '{ onClick: () => void; jser: string; }' is not assignable to type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'. Property 'jser' does not exist on type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'.B onClick ={props .onClick }={ jser props .jser }/>}functionB (props :React .ComponentProps <'div'>) {return <div {...props }/>}
tsx
importReact from 'react'functionA () {constprops = {onClick : () => {},jser : 'dev'}return <Type '{ onClick: () => void; jser: string; }' is not assignable to type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'. Property 'jser' does not exist on type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'.2322Type '{ onClick: () => void; jser: string; }' is not assignable to type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'. Property 'jser' does not exist on type 'IntrinsicAttributes & ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement>'.B onClick ={props .onClick }={ jser props .jser }/>}functionB (props :React .ComponentProps <'div'>) {return <div {...props }/>}
Considering JSX is merely a syntax suger, we can simply the repro code as below.
ts
typeA = {a : string}consta :A = {a : 'jser',Type '{ a: string; b: string; }' is not assignable to type 'A'. Object literal may only specify known properties, and 'b' does not exist in type 'A'.2322Type '{ a: string; b: string; }' is not assignable to type 'A'. Object literal may only specify known properties, and 'b' does not exist in type 'A'.b : 'dev'}constb = {a : 'jser',b : 'dev'}// Why doesn't following code get complained?constc :A = {...b }
ts
typeA = {a : string}consta :A = {a : 'jser',Type '{ a: string; b: string; }' is not assignable to type 'A'. Object literal may only specify known properties, and 'b' does not exist in type 'A'.2322Type '{ a: string; b: string; }' is not assignable to type 'A'. Object literal may only specify known properties, and 'b' does not exist in type 'A'.b : 'dev'}constb = {a : 'jser',b : 'dev'}// Why doesn't following code get complained?constc :A = {...b }
After doing a little search I found this issue, and got to know that this is by design.
When you spread in c, you don’t know what properties it really has. So TypeScript doesn’t really know if you have excess properties in some cases.
Yeah, I don’t know much about the TypeScript internals, but Flow does a great job alerting on this issue.

Before TypeScript improves on this issue, just remember that spread leads to unsound type in TypeScript, it is best to explicit type the variable before spreading.
tsx
importReact from 'react'functionA () {constprops :React .ComponentProps <typeofB > = {onClick : () => {},Type '{ onClick: () => void; jser: string; }' is not assignable to type '{ onClick: () => void; }'. Object literal may only specify known properties, and 'jser' does not exist in type '{ onClick: () => void; }'.2322Type '{ onClick: () => void; jser: string; }' is not assignable to type '{ onClick: () => void; }'. Object literal may only specify known properties, and 'jser' does not exist in type '{ onClick: () => void; }'.jser : 'dev'}return <B {...props }/>}functionB (props : {onClick :() => void}) {return <div {...props }/>}
tsx
importReact from 'react'functionA () {constprops :React .ComponentProps <typeofB > = {onClick : () => {},Type '{ onClick: () => void; jser: string; }' is not assignable to type '{ onClick: () => void; }'. Object literal may only specify known properties, and 'jser' does not exist in type '{ onClick: () => void; }'.2322Type '{ onClick: () => void; jser: string; }' is not assignable to type '{ onClick: () => void; }'. Object literal may only specify known properties, and 'jser' does not exist in type '{ onClick: () => void; }'.jser : 'dev'}return <B {...props }/>}functionB (props : {onClick :() => void}) {return <div {...props }/>}