Skip to content

Update typescript types, fix react-spring/addons, add react-spring/hooks #381

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 7 commits into from
Dec 18, 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
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
"main": "web.cjs.js",
"module": "web.js",
"react-native": "native.js",
"typings": "index.d.ts",
"private": true,
"sideEffects": false,
"scripts": {
"prebuild": "rimraf dist",
"build": "npm-run-all --parallel copy rollup",
"copy": "copyfiles package.json readme.md LICENSE.md index.d.ts dist && json -I -f dist/package.json -e \"this.private=false; this.devDependencies=undefined; this.scripts=undefined; this.husky=undefined; this.prettier=undefined; this.jest=undefined; this['lint-staged'] = undefined;\"",
"copy": "copyfiles -f package.json readme.md LICENSE.md \"types/*\" dist && json -I -f dist/package.json -e \"this.private=false; this.devDependencies=undefined; this.scripts=undefined; this.husky=undefined; this.prettier=undefined; this.jest=undefined; this['lint-staged'] = undefined;\"",
"rollup": "rollup -c",
"prepare": "npm run build",
"test": "jest",
Expand Down Expand Up @@ -72,7 +71,7 @@
"@babel/preset-react": "^7.0.0",
"@storybook/addon-knobs": "^3.4.11",
"@storybook/react": "^3.4.11",
"@types/react": "^16.7.8",
"@types/react": "^16.7.17",
"babel-core": "7.0.0-bridge.0",
"babel-jest": "^23.6.0",
"babel-plugin-react-docgen": "^2.0.0",
Expand All @@ -89,6 +88,7 @@
"immer-wieder": "^1.1.5",
"jest": "^23.6.0",
"json": "^9.0.6",
"konva": "^2.6.0",
"lint-staged": "^7.3.0",
"lorem-ipsum": "^1.0.6",
"mock-raf": "^1.0.0",
Expand All @@ -99,6 +99,7 @@
"react-dom": "16.7.0-alpha.2",
"react-feather": "^1.1.4",
"react-helmet": "^5.2.0",
"react-konva": "^16.6.31",
"react-test-renderer": "^16.5.2",
"react-testing-library": "^5.2.0",
"react-transition-group": "^2.5.0",
Expand Down
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"noFallthroughCasesInSwitch": true,
"noEmitOnError": true,
"forceConsistentCasingInFileNames": true,
"lib": ["es2016", "dom"]
"lib": ["es2016", "dom"],
"skipLibCheck": false
},
"include": ["tests/**/*.tsx"]
"include": ["tests/**/*.tsx", "types/**/*.ts"]
}
28 changes: 28 additions & 0 deletions types/addons.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Ref, PureComponent } from 'react'
import { SpringConfig } from './universal'

interface ParallaxProps {
pages: number

config?: SpringConfig | ((key: string) => SpringConfig)

scrolling?: boolean

horizontal?: boolean

ref?: Ref<Parallax>
}

export class Parallax extends PureComponent<ParallaxProps> {
scrollTo: (offset: number) => void
}

interface ParallaxLayerProps {
factor?: number

offset?: number

speed?: number
}

export class ParallaxLayer extends PureComponent<ParallaxLayerProps> {}
231 changes: 231 additions & 0 deletions types/hooks.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import {
SpringConfig,
SpringBaseProps,
TransitionKeyProps,
State,
} from './universal'
export { SpringConfig, SpringBaseProps, TransitionKeyProps, State }

/** List from `function getForwardProps` in `src/shared/helpers` */
type ExcludedProps =
| 'to'
| 'from'
| 'config'
| 'native'
| 'onStart'
| 'onRest'
| 'onFrame'
| 'children'
| 'reset'
| 'reverse'
| 'force'
| 'immediate'
| 'impl'
| 'inject'
| 'delay'
| 'attach'
| 'destroyed'
| 'track'
| 'interpolateTo'
| 'autoStart'
| 'ref'
export type ForwardedProps<T> = Pick<T, Exclude<keyof T, ExcludedProps>>
// NOTE: because of the Partial, this makes a weak type, which can have excess props
type InferFrom<T extends object> = T extends { to: infer TTo }
? Partial<TTo>
: Partial<ForwardedProps<T>>

// This is similar to "Omit<A, keyof B> & B",
// but with a delayed evaluation that still allows A to be inferrable
type Merge<A, B> = { [K in keyof A]: K extends keyof B ? B[K] : A[K] } & B

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.
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
}

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

export type UseSpringProps<DS extends UseSpringBaseProps> = Merge<
DS,
{
from?: InferFrom<DS>
/**
* Callback when the animation comes to a still-stand
*/
onRest?(ds: InferFrom<DS>): void
}
>

// there's a third value in the tuple but it's not public API (?)
export function useSpring<DS extends object>(
getProps: () => UseSpringProps<DS>
): [ForwardedProps<DS>, SetUpdateFn<DS>]
export function useSpring<DS extends object>(
values: UseSpringProps<DS>
): ForwardedProps<DS>

// there's a third value in the tuple but it's not public API (?)
export function useTrail<DS extends object>(
count: number,
getProps: () => UseSpringProps<DS>
): [ForwardedProps<DS>[], SetUpdateFn<DS>]
export function useTrail<DS extends object>(
count: number,
values: UseSpringProps<DS>
): ForwardedProps<DS>[] // safe to modify (result of .map)

export interface UseTransitionProps<TItem, DS extends object>
extends HooksBaseProps {
items: ReadonlyArray<TItem> | null | undefined
/**
* Spring config, or for individual items: fn(item => config)
* @default config.default
*/
config?: SpringConfig | ((item: TItem) => SpringConfig)
/**
* The same keys you would normally hand over to React in a list. Keys can be specified as a key-accessor function, an array of keys, or a single value
*/
keys?:
| ((item: TItem) => TransitionKeyProps)
| ReadonlyArray<TransitionKeyProps>
| TransitionKeyProps

/**
* When true enforces that an item can only occur once instead of allowing two or more items with the same key to co-exist in a stack
* @default false
*/
unique?: boolean
/**
* Trailing delay in ms
*/
trail?: number

from?: InferFrom<DS>
/**
* Values that apply to new elements, or: item => values
* @default {}
*/
enter?: InferFrom<DS> | ((item: TItem) => InferFrom<DS>)
/**
* Values that apply to leaving elements, or: item => values
* @default {}
*/
leave?: InferFrom<DS> | ((item: TItem) => InferFrom<DS>)
/**
* Values that apply to elements that are neither entering nor leaving (you can use this to update present elements), or: item => values
*/
update?: InferFrom<DS> | ((item: TItem) => InferFrom<DS>)
}

export interface UseTransitionResult<TItem, DS extends object> {
item: TItem
key: string
state: State
props: ForwardedProps<DS>
}

export function useTransition<TItem, DS extends object>(
values: Merge<DS, UseTransitionProps<TItem, DS>>
): UseTransitionResult<TItem, ForwardedProps<DS>>[] // result array is safe to modify

// higher order hooks 🤯
export namespace useKeyframes {
// this kind of higher order hook requires higher kinded types to type correctly at all
// this is the best approximation I can get to....... and there sure are anys around 🤯

// the docs says it receives a 3rd argument with the component's own props, but it's only
// ever called with two (https://github.com/react-spring/react-spring/blob/v7.1.3/src/hooks/KeyframesHook.js#L25)
export type KeyframeFn<DS extends object> = (
next: (props: UseSpringProps<DS>) => Promise<void>,
cancel: () => void
) => Promise<void>
export interface SpringKeyframeSlotsConfig extends HooksBaseProps {
// this is way too self-referential to type correctly
// both onRest and config have to have vague types... 😭

/**
* Callback when the animation comes to a still-stand
*/
onRest?(ds: any): void
config?:
| SpringConfig
| ReadonlyArray<SpringConfig>
| ((slot: string) => SpringConfig | ReadonlyArray<SpringConfig>)
}
export interface TrailKeyframeSlotsConfig<TItem>
extends SpringKeyframeSlotsConfig {
items: ReadonlyArray<TItem>
}

export type ResolveKeyframeSlotValue<T> = T extends KeyframeFn<infer V>
? V
: T extends ReadonlyArray<infer U>
? U extends KeyframeFn<infer V>
? V
: U extends Function // guard against accidentally putting in a KeyframeFn function
? never
: U
: T extends Function // same guard, but when the function is not in an array
? never
: T

// 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 object>(
animation:
| ReadonlyArray<DS>
| ReadonlyArray<KeyframeFn<DS>>
| KeyframeFn<DS>,
initialProps?: UseSpringProps<DS>
): UseSpringKeyframes<DS>
// 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
// `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>(
slots: TSlots & SpringKeyframeSlotsConfig,
// also unfortunately not possible to strongly type this either
initialProps?: UseSpringProps<any>
): UseSpringKeyframesWithSlots<TSlots>

export function trail<DS extends object>(
animation:
| ReadonlyArray<DS>
| ReadonlyArray<KeyframeFn<DS>>
| KeyframeFn<DS>,
initialProps?: UseSpringProps<DS>
): UseTrailKeyframes<DS>
export function trail<TItem, TSlots extends object>(
slots: TSlots & TrailKeyframeSlotsConfig<TItem>,
initialProps?: UseSpringProps<any>
): UseTrailKeyframesWithSlots<TSlots>

export type UseSpringKeyframesWithSlots<TSlots extends object> =
// there's a bug in the implementation that actually should cause a crash
// because there is no second argument, but because it is built with babel on loose mode,
// it doesn't...
<TSlot extends Exclude<keyof TSlots, keyof SpringKeyframeSlotsConfig>>(
slot: TSlot
) => ForwardedProps<ResolveKeyframeSlotValue<TSlots[TSlot]>>
// this one crashes "even more" because both values are undefined
export type UseSpringKeyframes<DS extends object> = () => ForwardedProps<DS>

export type UseTrailKeyframesWithSlots<TSlots extends object> = <
TSlot extends Exclude<keyof TSlots, keyof TrailKeyframeSlotsConfig<any>>
>(
count: number,
slot: TSlot
) => ForwardedProps<ResolveKeyframeSlotValue<TSlots[TSlot]>>[]

export type UseTrailKeyframes<DS extends object> = (
count: number
) => ForwardedProps<DS>[]
}
5 changes: 5 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// This is the file TypeScript will see when it looks inside the module without any sub-paths
// This should just reexport the package.json's "main".
export * from './web'
// Add this (and to all other entry points) if a default export is ever added
// export { default } from './web'
25 changes: 25 additions & 0 deletions types/konva.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
ForwardRefExoticComponent,
ComponentPropsWithRef,
ReactType,
} from 'react'
import { animated } from './universal'
export * from './universal'

import * as konva from 'react-konva'

type KonvaComponents = Pick<
typeof konva,
{
[K in keyof typeof konva]: typeof konva[K] extends ReactType ? K : never
}[keyof typeof konva]
>

declare const augmentedAnimated: typeof animated &
{
[Tag in keyof KonvaComponents]: ForwardRefExoticComponent<
ComponentPropsWithRef<KonvaComponents[Tag]>
>
}

export { augmentedAnimated as animated }
1 change: 1 addition & 0 deletions types/native.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './universal'
Loading