callstack / linaria

Zero-runtime CSS in JS library
https://linaria.dev
MIT License
11.46k stars 414 forks source link

Change `styled()` types so that it works with libraries like Framer Motion #1417

Open jalooc opened 1 month ago

jalooc commented 1 month ago

Describe the enhancement

When extending custom components, Linaria's types require that component to have the style prop [link] (kudos for setting that requirement in the type system - really helpful and solid 💪).

This works with usual custom components, but fails to work with e.g. Framer Motion. I believe this is a result of 2 things:

  1. Linaria requiring the styles prop to be CSSProperties [link] (which is fair),
  2. Framer Motion's style prop to be an extension of CSSProperties allowing for passing motion values to styles [link].

Granted, the problem lies probably more on the FM's extension of styles, but I figured it'd be easier for Linaria to relax the styles type requirement, considering that (if I understand correctly) the only purpose that Linaria requires styles is for is setting the custom properties. So the type could be relaxed to a mere Record<string, unknown> maybe?

Motivation

Framer Motion is a great animation library with an api allowing for fantastic components composition/extension with the api that Linaria (and similar css-in-js libs) provide. Would be great if Linaria's types worked with it seamlessly, just like e.g. Styled Components' types do.

jalooc commented 1 month ago

So the type could be relaxed to a mere Record<string, unknown> maybe?

It actually seems to be working - I used patch-package to apply this patch to @linaria/react@6.2.1:

diff --git a/node_modules/@linaria/react/types/styled.d.ts b/node_modules/@linaria/react/types/styled.d.ts
index c790c46..f6fa008 100644
--- a/node_modules/@linaria/react/types/styled.d.ts
+++ b/node_modules/@linaria/react/types/styled.d.ts
@@ -11,9 +11,9 @@ type Has<T, TObj> = [T] extends [TObj] ? T : T & TObj;
 export declare const omit: <T extends Record<string, unknown>, TKeys extends keyof T>(obj: T, keys: TKeys[]) => Omit<T, TKeys>;
 declare function styled(componentWithStyle: () => any): (error: 'The target component should have a className prop') => void;
 declare function styled<TProps extends Has<TMustHave, {
-    style?: React.CSSProperties;
+    style?: Record<string, unknown>;
 }>, TMustHave extends {
-    style?: React.CSSProperties;
+    style?: Record<string, unknown>;
 }, TConstructor extends Component<TProps>>(componentWithStyle: TConstructor & Component<TProps>): ComponentStyledTagWithInterpolation<TProps, TConstructor>;
 declare function styled<TProps extends Has<TMustHave, {
     className?: string;

and the problem is gone, while the style prop on Linaria components remains type-safe. I'll keep monitoring for the issue while writing more code / corner cases.