Skip to content

Commit

Permalink
Fix issues spreading omitted props onto components (#3313)
Browse files Browse the repository at this point in the history
* Simplify props types

wip

wip

* Remove unused types

* Remove test

it’s no longer relevant

* Update changelog
  • Loading branch information
thecrypticace committed Jun 24, 2024
1 parent d60ed6a commit a593d19
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 39 deletions.
4 changes: 3 additions & 1 deletion packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Nothing yet!
### Fixed

- Fix issues spreading omitted props onto components ([#3313](https://github.com/tailwindlabs/headlessui/pull/3313))

## [2.1.0] - 2024-06-21

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
leaveFrom,
leaveTo,

// @ts-expect-error
...theirProps
} = props as typeof props
let container = useRef<HTMLElement | null>(null)
Expand Down Expand Up @@ -444,6 +443,8 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
className:
classNames(
// Incoming classes if any
// @ts-expect-error: className may not exist because not
// all components accept className (but all HTML elements do)
theirProps.className,

// Apply these classes immediately
Expand Down Expand Up @@ -498,7 +499,6 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
props: TransitionRootProps<TTag>,
ref: Ref<HTMLElement>
) {
// @ts-expect-error
let { show, appear = false, unmount = true, ...theirProps } = props as typeof props
let internalTransitionRef = useRef<HTMLElement | null>(null)
let requiresRef = shouldForwardRef(props)
Expand Down Expand Up @@ -610,10 +610,8 @@ function ChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>
return (
<>
{!hasTransitionContext && hasOpenClosedContext ? (
// @ts-expect-error This is an object
<TransitionRoot ref={ref} {...props} />
) : (
// @ts-expect-error This is an object
<InternalTransitionChild ref={ref} {...props} />
)}
</>
Expand Down
17 changes: 0 additions & 17 deletions packages/@headlessui-react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ import type { JSXElementConstructor, ReactElement, ReactNode } from 'react'

export type ReactTag = keyof JSX.IntrinsicElements | JSXElementConstructor<any>

// A unique placeholder we can use as a default. This is nice because we can use this instead of
// defaulting to null / never / ... and possibly collide with actual data.
// Ideally we use a unique symbol here.
let __ = '1D45E01E-AF44-47C4-988A-19A94EBAF55C' as const
export type __ = typeof __

export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never

export type PropsOf<TTag extends ReactTag> = TTag extends React.ElementType
Expand Down Expand Up @@ -55,15 +49,4 @@ export type Props<
ClassNameOverride<TTag, TSlot> &
Overrides

type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never }
export type XOR<T, U> = T | U extends __
? never
: T extends __
? U
: U extends __
? T
: T | U extends object
? (Without<T, U> & U) | (Without<U, T> & T)
: T | U

export type EnsureArray<T> = T extends any[] ? T : Expand<T>[]
10 changes: 0 additions & 10 deletions packages/@headlessui-react/src/utils/render.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -423,16 +423,6 @@ describe('Features.Static | Features.RenderStrategy', () => {
)
}

// TODO: Can we "legit" test this? 🤔
it('should result in a typescript error', () => {
testRender(
// @ts-expect-error static & unmount together are incompatible
<Dummy show={false} static unmount>
Contents
</Dummy>
)
})

// To avoid duplication, and to make sure that the features tested in isolation can also be
// re-used when they are combined.
testStaticFeature(Dummy)
Expand Down
18 changes: 11 additions & 7 deletions packages/@headlessui-react/src/utils/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
type ReactElement,
type Ref,
} from 'react'
import type { Expand, Props, XOR, __ } from '../types'
import type { Expand, Props } from '../types'
import { classNames } from './class-names'
import { match } from './match'

Expand Down Expand Up @@ -40,17 +40,21 @@ export enum RenderStrategy {
Hidden,
}

type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any
? R
: never

type PropsForFeature<
TPassedInFeatures extends RenderFeatures,
TForFeature extends RenderFeatures,
TProps,
> = {
[P in TPassedInFeatures]: P extends TForFeature ? TProps : __
}[TPassedInFeatures]
> = TPassedInFeatures extends TForFeature ? TProps : {}

export type PropsForFeatures<T extends RenderFeatures> = XOR<
PropsForFeature<T, RenderFeatures.Static, { static?: boolean }>,
PropsForFeature<T, RenderFeatures.RenderStrategy, { unmount?: boolean }>
export type PropsForFeatures<T extends RenderFeatures> = Expand<
UnionToIntersection<
| PropsForFeature<T, RenderFeatures.Static, { static?: boolean }>
| PropsForFeature<T, RenderFeatures.RenderStrategy, { unmount?: boolean }>
>
>

export function render<TFeature extends RenderFeatures, TTag extends ElementType, TSlot>({
Expand Down

0 comments on commit a593d19

Please sign in to comment.