Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting prop via .attrs() is not making the prop on the final component optional #4314

Open
AntonNiklasson opened this issue May 20, 2024 · 2 comments

Comments

@AntonNiklasson
Copy link

AntonNiklasson commented May 20, 2024

Related to #4302, #4303

I've been trying to understand what's missing in the typing for .attrs(). In #4306 I added a failing test case that highlights the issue we're seeing.

But I also found a bug in the test code: the tests relevant to this logic are actually wrong. Here's the diff that fixes the test:

@@ -210,7 +210,7 @@ interface MyStyle extends StyledObject<object> {
   textTransform?: StyledObject<object>['textTransform'];
 }
 
-const DivWithRequiredProps = styled.div.attrs<{ foo?: number; bar: string }>({
+const DivWithRequiredProps = styled.div.attrs<{ foo: number; bar: string }>({
   foo: 42, // Providing required prop foo, which makes it optional
 })``;
 
@@ -221,7 +221,7 @@ const DivWithRequiredProps = styled.div.attrs<{ foo?: number; bar: string }>({
 // @ts-expect-error and bar is still required
 <DivWithRequiredProps />;
 
-const DivWithUnfulfilledRequiredProps = styled.div<{ foo?: number; bar: string }>``;
+const DivWithUnfulfilledRequiredProps = styled.div<{ foo: number; bar: string }>``;
 
 // same test as above but with a wrapped, typed component
 const RequiredPropsProvidedAsAttrs = styled(DivWithUnfulfilledRequiredProps).attrs({

When I make this change, it surfaces the issue I'm seeing in my initial test case. With proper typing, foo should be optional here, but it's not:

Screenshot 2024-05-19 at 23 03 02

@AntonNiklasson
Copy link
Author

AntonNiklasson commented May 20, 2024

I've been toying with an idea to fix this, but I'm really in deep waters here 😊

What I think is missing is a piece of logic that just takes the props that was actually passed in to attrs and makes them optional in the final thing:

Something like this:

type ConsiderAttrs<
  Props extends object,
  Attrs extends object | ((...args: any[]) => any),
> = Attrs extends (...args: any[]) => infer ReturnedAttrs
  ? Partial<ReturnedAttrs> & Omit<Props, keyof ReturnedAttrs>
  : Partial<Attrs> & Omit<Props, keyof Attrs>;

// ...

PrivateResolvedTarget extends KnownTarget
  ? ConsiderAttrs<
      Substitute<
        Substitute<OuterProps, React.ComponentPropsWithRef<PrivateResolvedTarget>>,
        Props
      >,
      PrivateAttrsArg
    >
  : PrivateMergedProps,

That partly fixes the issue, but doesn't fully support a generic type to attrs where only parts of it was actually passed, like this:

const Component = styled.div.attrs<{ foo: number; bar: string }>({
  foo: 42
})``;

// @ts-expect-error
<DivWithRequiredProps />;

This should error, but doesn't with my addition.

@Martinocom-Switcho
Copy link

Martinocom-Switcho commented Jun 11, 2024

It was also asked here and marked as solved but then reverted. This .attrs wrong typing is holding us from updating to 6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants