From 85e3f713e056832c0479095bd219a37dbacc0a60 Mon Sep 17 00:00:00 2001 From: Andre Wiggins Date: Tue, 12 Dec 2023 16:37:20 -0800 Subject: [PATCH] Use `unknown` instead of `any` when we explicitly don't know the type Also improve typings of options used in hooks --- hooks/src/index.js | 52 +++++++++++++++++++++-------------- hooks/src/internal.d.ts | 60 ++++++++++++++++++++--------------------- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/hooks/src/index.js b/hooks/src/index.js index 5544503d44..2e4a066ba7 100644 --- a/hooks/src/index.js +++ b/hooks/src/index.js @@ -18,7 +18,7 @@ let afterPaintEffects = []; let EMPTY = []; // Cast to use internal Options type -const options = /** @type {Options} */ (_options); +const options = /** @type {import('./internal').Options} */ (_options); let oldBeforeDiff = options._diff; let oldBeforeRender = options._render; @@ -29,11 +29,13 @@ let oldBeforeUnmount = options.unmount; const RAF_TIMEOUT = 100; let prevRaf; +/** @type {(vnode: import('./internal').VNode) => void} */ options._diff = vnode => { currentComponent = null; if (oldBeforeDiff) oldBeforeDiff(vnode); }; +/** @type {(vnode: import('./internal').VNode) => void} */ options._render = vnode => { if (oldBeforeRender) oldBeforeRender(vnode); @@ -62,6 +64,7 @@ options._render = vnode => { previousComponent = currentComponent; }; +/** @type {(vnode: import('./internal').VNode) => void} */ options.diffed = vnode => { if (oldAfterDiff) oldAfterDiff(vnode); @@ -82,6 +85,7 @@ options.diffed = vnode => { previousComponent = currentComponent = null; }; +/** @type {(vnode: import('./internal').VNode) => void} */ options._commit = (vnode, commitQueue) => { commitQueue.some(component => { try { @@ -101,6 +105,7 @@ options._commit = (vnode, commitQueue) => { if (oldCommit) oldCommit(vnode, commitQueue); }; +/** @type {(vnode: import('./internal').VNode) => void} */ options.unmount = vnode => { if (oldBeforeUnmount) oldBeforeUnmount(vnode); @@ -127,11 +132,7 @@ options.unmount = vnode => { */ function getHookState(index, type) { if (options._hook) { - options._hook( - /** @type {any} */ (currentComponent), - index, - currentHook || type - ); + options._hook(currentComponent, index, currentHook || type); } currentHook = 0; @@ -155,7 +156,8 @@ function getHookState(index, type) { } /** - * @param {import('./index').StateUpdater} [initialState] + * @template {unknown} S + * @param {import('./index').StateUpdater} [initialState] */ export function useState(initialState) { currentHook = 1; @@ -163,10 +165,12 @@ export function useState(initialState) { } /** - * @param {import('./index').Reducer} reducer - * @param {import('./index').StateUpdater} initialState + * @template {unknown} S + * @template {unknown} A + * @param {import('./index').Reducer} reducer + * @param {import('./index').StateUpdater} initialState * @param {(initialState: any) => void} [init] - * @returns {[ any, (state: any) => void ]} + * @returns {[ S, (state: S) => void ]} */ export function useReducer(reducer, initialState, init) { /** @type {import('./internal').ReducerHookState} */ @@ -268,7 +272,7 @@ export function useReducer(reducer, initialState, init) { /** * @param {import('./internal').Effect} callback - * @param {any[]} args + * @param {unknown[]} args */ export function useEffect(callback, args) { /** @type {import('./internal').EffectHookState} */ @@ -283,7 +287,7 @@ export function useEffect(callback, args) { /** * @param {import('./internal').Effect} callback - * @param {any[]} args + * @param {unknown[]} args */ export function useLayoutEffect(callback, args) { /** @type {import('./internal').EffectHookState} */ @@ -296,6 +300,7 @@ export function useLayoutEffect(callback, args) { } } +/** @type {(initialValue: unknown) => unknown} */ export function useRef(initialValue) { currentHook = 5; return useMemo(() => ({ current: initialValue }), []); @@ -304,7 +309,7 @@ export function useRef(initialValue) { /** * @param {object} ref * @param {() => object} createHandle - * @param {any[]} args + * @param {unknown[]} args */ export function useImperativeHandle(ref, createHandle, args) { currentHook = 6; @@ -323,8 +328,8 @@ export function useImperativeHandle(ref, createHandle, args) { } /** - * @param {() => any} factory - * @param {any[]} args + * @param {() => unknown} factory + * @param {unknown[]} args */ export function useMemo(factory, args) { /** @type {import('./internal').MemoHookState} */ @@ -341,7 +346,7 @@ export function useMemo(factory, args) { /** * @param {() => void} callback - * @param {any[]} args + * @param {unknown[]} args */ export function useCallback(callback, args) { currentHook = 8; @@ -384,7 +389,7 @@ export function useDebugValue(value, formatter) { } /** - * @param {(error: any, errorInfo: import('preact').ErrorInfo) => void} cb + * @param {(error: unknown, errorInfo: import('preact').ErrorInfo) => void} cb */ export function useErrorBoundary(cb) { /** @type {import('./internal').ErrorBoundaryHookState} */ @@ -405,6 +410,7 @@ export function useErrorBoundary(cb) { ]; } +/** @type {() => string} */ export function useId() { /** @type {import('./internal').IdHookState} */ const state = getHookState(currentIndex++, 11); @@ -482,7 +488,7 @@ function afterPaint(newQueueLength) { } /** - * @param {import('./internal').EffectHookState} hook + * @param {import('./internal').HookState} hook */ function invokeCleanup(hook) { // A hook cleanup can introduce a call to render which creates a new root, this will call options.vnode @@ -510,8 +516,8 @@ function invokeEffect(hook) { } /** - * @param {any[]} oldArgs - * @param {any[]} newArgs + * @param {unknown[]} oldArgs + * @param {unknown[]} newArgs */ function argsChanged(oldArgs, newArgs) { return ( @@ -521,6 +527,12 @@ function argsChanged(oldArgs, newArgs) { ); } +/** + * @template Arg + * @param {Arg} arg + * @param {(arg: Arg) => any} f + * @returns {any} + */ function invokeOrReturn(arg, f) { return typeof f == 'function' ? f(arg) : f; } diff --git a/hooks/src/internal.d.ts b/hooks/src/internal.d.ts index 2e4e7427d9..9e398345a7 100644 --- a/hooks/src/internal.d.ts +++ b/hooks/src/internal.d.ts @@ -1,23 +1,19 @@ -import { Reducer } from '.'; +import { Reducer, StateUpdater } from '.'; export { PreactContext }; -/** - * The type of arguments passed to a Hook function. While this type is not - * strictly necessary, they are given a type name to make it easier to read - * the following types and trace the flow of data. - */ -export type HookArgs = any; - -/** - * The return type of a Hook function. While this type is not - * strictly necessary, they are given a type name to make it easier to read - * the following types and trace the flow of data. - */ -export type HookReturnValue = any; - -/** The public function a user invokes to use a Hook */ -export type Hook = (...args: HookArgs[]) => HookReturnValue; +export interface Options extends globalThis.Options { + /** Attach a hook that is invoked before a vnode is diffed. */ + _diff?(vnode: VNode): void; + diffed?(vnode: VNode): void; + /** Attach a hook that is invoked before a vnode has rendered. */ + _render?(vnode: VNode): void; + /** Attach a hook that is invoked after a tree was mounted or was updated. */ + _commit?(vnode: VNode, commitQueue: Component[]): void; + _unmount?(vnode: VNode): void; + /** Attach a hook that is invoked before a hook's state is queried. */ + _hook?(component: Component, index: number, type: HookType): void; +} // Hook tracking @@ -37,6 +33,7 @@ export interface Component extends globalThis.Component { export interface VNode extends globalThis.VNode { _mask?: [number, number]; + _component?: Component; // Override with our specific Component type } export type HookState = @@ -49,10 +46,12 @@ export type HookState = interface BaseHookState { _value?: unknown; - _component?: undefined; _nextValue?: undefined; _pendingValue?: undefined; + _args?: undefined; _pendingArgs?: undefined; + _component?: undefined; + _cleanup?: undefined; } export type Effect = () => void | Cleanup; @@ -60,24 +59,25 @@ export type Cleanup = () => void; export interface EffectHookState extends BaseHookState { _value?: Effect; - _args?: any[]; - _pendingArgs?: any[]; + _args?: unknown[]; + _pendingArgs?: unknown[]; _cleanup?: Cleanup | void; } export interface MemoHookState extends BaseHookState { - _value?: any; - _pendingValue?: any; - _args?: any[]; - _pendingArgs?: any[]; - _factory?: () => any; + _value?: unknown; + _pendingValue?: unknown; + _args?: unknown[]; + _pendingArgs?: unknown[]; + _factory?: () => unknown; } -export interface ReducerHookState extends BaseHookState { - _nextValue?: any; - _value?: any; +export interface ReducerHookState + extends BaseHookState { + _nextValue?: [S, StateUpdater]; + _value?: [S, StateUpdater]; _component?: Component; - _reducer?: Reducer; + _reducer?: Reducer; } export interface ContextHookState extends BaseHookState { @@ -87,7 +87,7 @@ export interface ContextHookState extends BaseHookState { } export interface ErrorBoundaryHookState extends BaseHookState { - _value?: (error: any, errorInfo: ErrorInfo) => void; + _value?: (error: unknown, errorInfo: ErrorInfo) => void; } export interface IdHookState extends BaseHookState {