Skip to content

More TypeScript updates #398

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

Merged
merged 4 commits into from
Dec 30, 2018
Merged
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
79 changes: 75 additions & 4 deletions types/hooks.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CSSProperties, RefObject } from 'react'
import {
SpringConfig,
SpringBaseProps,
Expand All @@ -6,6 +7,10 @@ import {
} from './universal'
export { SpringConfig, SpringBaseProps, TransitionKeyProps, State }

export { config, interpolate } from './universal'
// hooks are currently web-only
export { animated } from './web'

/** List from `function getForwardProps` in `src/shared/helpers` */
type ExcludedProps =
| 'to'
Expand Down Expand Up @@ -43,18 +48,34 @@ export type SetUpdateFn<DS extends object> = (ds: DS) => void

// The hooks do emulate React's 'ref' by accepting { ref?: React.RefObject<Controller> } and
// updating it. However, there are no types for Controller, and I assume it is intentionally so.
// This is a partial interface for Controller that has only the properties needed for useChain to work.
export interface ReactSpringHook {
start(): void
stop(): void
}

export function useChain(refs: ReadonlyArray<RefObject<ReactSpringHook>>): void
// this looks like it can just be a single overload, but we don't want to allow
// timeFrame to be specifiable when timeSteps is explicitly "undefined"
export function useChain(
refs: ReadonlyArray<RefObject<ReactSpringHook>>,
timeSteps: number[],
timeFrame?: number
): void

export interface HooksBaseProps
extends Pick<SpringBaseProps, Exclude<keyof SpringBaseProps, 'config'>> {
// there is an undocumented onKeyframesHalt which passes the controller instance,
// so it also cannot be typed unless Controller types are written
ref?: React.RefObject<ReactSpringHook>
}

export interface UseSpringBaseProps extends HooksBaseProps {
config?: SpringBaseProps['config']
}

export type UseSpringProps<DS extends UseSpringBaseProps> = Merge<
DS,
export type UseSpringProps<DS extends object> = Merge<
DS & UseSpringBaseProps,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change fixes autocomplete so it shows UseSpringBaseProps' properties on autocomplete

{
from?: InferFrom<DS>
/**
Expand All @@ -65,6 +86,12 @@ export type UseSpringProps<DS extends UseSpringBaseProps> = Merge<
>

// there's a third value in the tuple but it's not public API (?)
export function useSpring<DS extends CSSProperties>(
values: UseSpringProps<DS & CSSProperties>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trick lets CSSProperties show up on autocomplete by default (as long as no property that you add is incompatible with CSSProperties itself; then it falls back to the other overloads)

): ForwardedProps<DS>
export function useSpring<DS extends CSSProperties>(
getProps: () => UseSpringProps<DS & CSSProperties>
): [ForwardedProps<DS>, SetUpdateFn<DS>]
export function useSpring<DS extends object>(
getProps: () => UseSpringProps<DS>
): [ForwardedProps<DS>, SetUpdateFn<DS>]
Expand All @@ -73,6 +100,14 @@ export function useSpring<DS extends object>(
): ForwardedProps<DS>

// there's a third value in the tuple but it's not public API (?)
export function useTrail<DS extends CSSProperties>(
count: number,
getProps: () => UseSpringProps<DS & CSSProperties>
): [ForwardedProps<DS>[], SetUpdateFn<DS>]
export function useTrail<DS extends CSSProperties>(
count: number,
values: UseSpringProps<DS & CSSProperties>
): ForwardedProps<DS>[] // safe to modify (result of .map)
export function useTrail<DS extends object>(
count: number,
getProps: () => UseSpringProps<DS>
Expand Down Expand Up @@ -132,6 +167,9 @@ export interface UseTransitionResult<TItem, DS extends object> {
props: ForwardedProps<DS>
}

export function useTransition<TItem, DS extends CSSProperties>(
values: Merge<DS & CSSProperties, UseTransitionProps<TItem, DS>>
): UseTransitionResult<TItem, ForwardedProps<DS>>[] // result array is safe to modify
export function useTransition<TItem, DS extends object>(
values: Merge<DS, UseTransitionProps<TItem, DS>>
): UseTransitionResult<TItem, ForwardedProps<DS>>[] // result array is safe to modify
Expand Down Expand Up @@ -179,30 +217,63 @@ export namespace useKeyframes {

// fun fact: the state is literally named "undefined" if you're using this overload
// the docs are vague but it seems this just loops the one animation forever?
export function spring<DS extends CSSProperties>(
animation:
| ReadonlyArray<DS & CSSProperties>
| ReadonlyArray<KeyframeFn<DS & CSSProperties>>
| KeyframeFn<DS & CSSProperties>,
initialProps?: UseSpringProps<DS & CSSProperties>
): UseSpringKeyframes<DS & CSSProperties>
export function spring<DS extends object>(
animation:
| ReadonlyArray<DS>
| ReadonlyArray<KeyframeFn<DS>>
| KeyframeFn<DS>,
initialProps?: UseSpringProps<DS>
): UseSpringKeyframes<DS>
// this overload does provide CSSProperties autocompletes, but only as long as config, delay, onRest
// et al are not specified. It's not possible to say TSlotNames can be
// "string but not keyof SpringKeyframeSlotsConfig".
export function spring<
TSlotNames extends string,
TSlots extends Record<TSlotNames, CSSProperties>
>(
slots: TSlots &
Record<TSlotNames, CSSProperties> &
SpringKeyframeSlotsConfig,
initialProps?: UseSpringProps<CSSProperties>
): UseSpringKeyframesWithSlots<TSlots>
// unfortunately, it's not possible to infer the type of the callback functions (if any are given)
// while also remaining possible to infer the slot names. Callback functions have to be cast with
// while also remaining possible to infer the slot contents. Callback functions have to be cast with
// `as useKeyframes.KeyframeFn<{ ... }>`.
// it's also not possible to specify the types of the values inside TSlots. This is a mess.
export function spring<TSlots extends object>(
export function spring<
TSlotNames extends string,
TSlots extends Record<TSlotNames, any>
>(
slots: TSlots & SpringKeyframeSlotsConfig,
// also unfortunately not possible to strongly type this either
initialProps?: UseSpringProps<any>
): UseSpringKeyframesWithSlots<TSlots>

export function trail<DS extends CSSProperties>(
animation:
| ReadonlyArray<DS & CSSProperties>
| ReadonlyArray<KeyframeFn<DS & CSSProperties>>
| KeyframeFn<DS & CSSProperties>,
initialProps?: UseSpringProps<DS>
): UseTrailKeyframes<DS>
export function trail<DS extends object>(
animation:
| ReadonlyArray<DS>
| ReadonlyArray<KeyframeFn<DS>>
| KeyframeFn<DS>,
initialProps?: UseSpringProps<DS>
): UseTrailKeyframes<DS>
// we unfortunately can't use the same trick in trail as we did in spring to have autocomplete for
// CSS properties, for the same reason the spring trick stops working when a config key is given.
// It would fail as soon as `items`, a required property, is specified, as its value
// (necessarily an array-like) would not be compatible with CSSProperties.
export function trail<TItem, TSlots extends object>(
slots: TSlots & TrailKeyframeSlotsConfig<TItem>,
initialProps?: UseSpringProps<any>
Expand Down
7 changes: 1 addition & 6 deletions types/universal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export interface SpringBaseProps {
onStart?(): void
}

export interface SpringProps<DS extends object = {}> {
export interface SpringProps<DS extends object = {}> extends SpringBaseProps {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this to address #386

/**
* Base styles
* @default {}
Expand All @@ -87,11 +87,6 @@ export interface SpringProps<DS extends object = {}> {
* Takes a function that receives interpolated styles
*/
children?: SpringRendererFunc<DS>
/**
* Prevents animation if true, you can also pass individual keys
* @default false
*/
immediate?: boolean | string[] | ((key: string) => boolean)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did immediate ever actually accept string[]? I can't find it in the code (in fact if it's an array it's just treated as always true in the Controller). This made it incompatible with SpringBaseProps so I had to remove the string[].

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kovensky No, immediate can be a boolean or a function returning boolean. This could be a left over.

/**
* Inject props
* @default undefined
Expand Down