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

Fix TypeScript error when writing higher order components while using Emotion's JSX namespace #3293

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nervous-pigs-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/react': patch
---

Fix TypeScript error when writing higher order components while using `@emotion/react` `jsx` pragma/`jsxImportSource`
19 changes: 11 additions & 8 deletions packages/react/src/jsx-namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ type IsPreReact19 = 2 extends Parameters<React.FunctionComponent<any>>['length']
? true
: false

type WithConditionalCSSProp<P> = 'className' extends keyof P
? string extends P['className' & keyof P]
? { css?: Interpolation<Theme> }
: {}
: {}
type WithConditionalCSSProp<P> =
| (P extends unknown
? 'className' extends keyof P
? string extends P['className' & keyof P]
? { css?: Interpolation<Theme> }
: {}
: {}
: {})
| {}

// unpack all here to avoid infinite self-referencing when defining our own JSX namespace for the pre-React 19 case

Expand Down Expand Up @@ -91,9 +95,8 @@ export namespace EmotionJSX {
export interface ElementChildrenAttribute
extends ReactJSXElementChildrenAttribute {}

export type LibraryManagedAttributes<C, P> = P extends unknown
? WithConditionalCSSProp<P> & ReactJSXLibraryManagedAttributes<C, P>
: never
export type LibraryManagedAttributes<C, P> = WithConditionalCSSProp<P> &
ReactJSXLibraryManagedAttributes<C, P>

export interface IntrinsicAttributes extends ReactJSXIntrinsicAttributes {}
export interface IntrinsicClassAttributes<T>
Expand Down
60 changes: 60 additions & 0 deletions packages/react/types/tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,63 @@ const anim1 = keyframes`
// $ExpectError
;<WithOptionalUndefinedClassName css={{ color: 'hotpink' }} />
}

{
const withSomething = <Props extends object>(
SomeComponent: (props: Props & { something: string }) => React.ReactNode
) => {
return (props: Props) => {
return <SomeComponent something="something" {...props} />
}
}
const RendersSomething = withSomething(props => <div>{props.something}</div>)
;<RendersSomething />

const WithSomeStyle = <Props extends { className?: string }>(
SomeComponent: (props: Props) => React.ReactNode
) => {
return (props: Props) => {
return (
<SomeComponent
{...props}
// this expect type is important over this just not erroring because an excess property in this case is allowed
// but this lets us roughly test that `css` is in autocomplete/etc.
// $ExpectType Interpolation<Theme>
css={{ direction: 'rtl' }}
/>
)
}
}
const Something = WithSomeStyle((props: { className?: string }) => null)
;<Something css={{ color: 'green' }} />
;<Props extends { className?: string }>(
SomeComponent: (props: Props) => React.ReactNode
) => {
return (props: Props) => {
return (
<SomeComponent
{...props}
css={{
// ideally this would error but it also doesn't matter much
// (this particular css={{ direction: 'does not exist' }} does error when used normally)
direction: 'does not exist'
}}
/>
)
}
}
;<Props extends object>(SomeComponent: (props: Props) => React.ReactNode) => {
return (props: Props) => {
return (
<SomeComponent
{...props}
// this isn't an error because excess properties is a best effort thing in ts etc.
// (and in this case with a generic, not erroring here makes a lot of sense)
// but expecting this type is a way to test that `css` isn't in autocomplete/etc.
// $ExpectType {}
css={{}}
/>
)
}
}
}
Loading