react-spring
react-spring copied to clipboard
Typescript: Assigning useTransition return value to a prop with type CSSProperties results in 'not assignable' error
🐛 Bug Report
The returned value from useTransition styles is not assignable to a prop with type CSSProperties. The full error is:
TS2322: Type '{ opacity: SpringValue<number>; }' is not assignable to type 'CSSProperties'. Types of property 'opacity' are incompatible. Type 'SpringValue<number>' is not assignable to type 'Opacity | undefined'. Type 'SpringValue<number>' is not assignable to type 'number & {}'. Type 'SpringValue<number>' is not assignable to type 'number'.
This is probably related to https://github.com/pmndrs/react-spring/issues/1102, it seems to keep happening under this scenario.
To Reproduce
Steps to reproduce the behavior: Using this code (or any component which receives styles as a prop with type CSSProperties):
const AnimatedTest = ({ styles }: { styles: CSSProperties }) => (
<animated.div style={styles}>Test</animated.div>
);
export default function App() {
const [items, setItems] = useState([]);
const transitions = useTransition(items, {
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 },
delay: 200,
});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{transitions((styles) => <AnimatedTest styles={styles}/>)}
</div>
);
}
Expected behavior
There should be no compilation error since the type returned from useTransition should be compatible with CSSProperties.
Link to repro (highly encouraged)
Environment
-
react-spring
v9.2.4 -
react
v17.0.3
Please create a reproduction of the issue in a sandbox for review.
I'm sorry, the link was wrong, I meant to link to this sandbox, which has the issue.
This workaround helped me in the meantime
const dropdown = useSpring({
opacity: (showMenu ? 1 : 0) as React.CSSProperties["opacity"],
pointerEvents: (showMenu
? "all"
: "none") as React.CSSProperties["pointerEvents"],
})
Should it be assignable to CSSProperties? You can resolve this by passing SpringValues
type. See https://codesandbox.io/s/react-spring-type-bug-forked-netbu?file=/src/App.tsx
Thanks @joshuaellis that looks like an even-cleaner solution.
@DamianPereira it'd be good to hear your thoughts on why you think it's an issue since we're not passing CSSProperties, we're passing SpringValues.
@joshuaellis My 2 cents are that it should work without needing to cast or specify the type, which would mean returning the correct SpringValues
by default 🙂 But this may be challenging to implement.
It does return SpringValues by default. The original issue is demonstrating that passing the props to another component means you have to cast it with SpringValues not CSSProperties. Unless you have a separate issue @DerekLoop?
Hi @joshuaellis ! Thanks for the help. Maybe I'm just not understanding the types correctly.
This example works in TypeScript:
import { animated as a, SpringValues, useSpring } from "@react-spring/web"
const dropdown = useSpring({
opacity: (showMenu ? 1 : 0) as React.CSSProperties["opacity"],
pointerEvents: (showMenu
? "all"
: "none") as React.CSSProperties["pointerEvents"],
})
return (<a.div style={dropdown}>Content!</a.div>)
This example gives an error:
import { animated as a, SpringValues, useSpring } from "@react-spring/web"
const dropdown = useSpring({
opacity: showMenu ? 1 : 0,
pointerEvents: showMenu ? "all" : "none",
})
return (<a.div style={dropdown}>Content!</a.div>)
This is the error:
Type '{ opacity: SpringValue<number>; pointerEvents: SpringValue<string>; }' is not assignable to type '{ alignContent?: "initial" | "end" | (string & {}) | "baseline" | "inherit" | "start" | "center" | "-moz-initial" | "revert" | "unset" | "normal" | "space-around" | "space-between" | ... 5 more ... | undefined; ... 802 more ...; matrix3d?: AnimatedObject<...> | undefined; }'.
Types of property 'pointerEvents' are incompatible.
Type 'SpringValue<string>' is not assignable to type '"initial" | "all" | "none" | "fill" | "stroke" | "auto" | "inherit" | "-moz-initial" | "revert" | "unset" | "painted" | "visible" | "visibleFill" | "visiblePainted" | "visibleStroke" | FluidValue<...> | undefined'.
Type 'SpringValue<string>' is not assignable to type 'FluidValue<NonObject<PointerEvents | undefined>, any>'.
The types returned by 'get()' are incompatible between these types.
Type 'string' is not assignable to type 'NonObject<PointerEvents | undefined>'.ts(2322)
index.d.ts(1841, 9): The expected type comes from property 'style' which is declared here on type 'IntrinsicAttributes & AnimatedProps<{ onChange?: FormEventHandler<HTMLDivElement> | undefined; onPause?: ReactEventHandler<HTMLDivElement> | undefined; ... 253 more ...; ref?: RefObject<...> | ... 2 more ... | undefined; }> & { ...; }'
(JSX attribute) style?: {
alignContent?: "end" | (string & {}) | "baseline" | "inherit" | "initial" | "start" | "center" | "-moz-initial" | "revert" | "unset" | "normal" | "space-around" | "space-between" | ... 5 more ... | undefined;
... 802 more ...;
matrix3d?: AnimatedObject<...> | undefined;
} | undefined
This example also works without an error:
import { animated as a, SpringValues, useSpring } from "@react-spring/web"
const dropdown = useSpring({
opacity: showMenu ? 1 : 0,
pointerEvents: showMenu ? "all" : "none",
}) as SpringValues<{ opacity: number; pointerEvents: "all" | "none" }>
return (<a.div style={dropdown}>Content!</a.div>)
I'm just not sure why casting is necessary in either case 🎃😅 Though the SpringValues solution seems more correct.