From 761759c52047fff6d786e93ce88dc55eaeee1e41 Mon Sep 17 00:00:00 2001 From: Meghea Iulian Date: Fri, 26 Jul 2024 17:04:17 +0300 Subject: [PATCH 1/5] Improve the typing of `useState` hook. --- src/core.ts | 8 ++++++++ src/use-state.ts | 41 +++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/core.ts b/src/core.ts index 6fc3b5a..19b7510 100644 --- a/src/core.ts +++ b/src/core.ts @@ -58,3 +58,11 @@ export { State } from "./state"; export type { Ref } from "./use-ref"; export type { Options as ComponentOptions } from "./component"; + +export type { + StateUpdater, + InitialState, + NewState, + StateTuple, + UseState, +} from "./use-state.ts"; diff --git a/src/use-state.ts b/src/use-state.ts index cefcd24..ed37423 100644 --- a/src/use-state.ts +++ b/src/use-state.ts @@ -1,38 +1,44 @@ import { hook, Hook } from "./hook"; import { State } from "./state"; -type NewState = T | ((previousState?: T) => T); -type StateUpdater = (value: NewState) => void; +export type InitialState = T | (() => T); +export type NewState = T | ((previousState: T) => T); +export type StateUpdater = (value: NewState) => void; +export type StateTuple = readonly [T, StateUpdater]; + +export interface UseState { + (): StateTuple; + (value?: InitialState): StateTuple; +} /** * @function * @template {*} T * @param {T} [initialState] - Optional initial state - * @return {readonly [state: T, updaterFn: StateUpdater]} stateTuple - Tuple of current state and state updater function + * @return {StateTuple} stateTuple - Tuple of current state and state updater function */ const useState = hook( class extends Hook { - args!: readonly [T, StateUpdater]; + args!: StateTuple; - constructor(id: number, state: State, initialValue: T) { + constructor(id: number, state: State, initialValue: InitialState) { super(id, state); this.updater = this.updater.bind(this); - - if (typeof initialValue === "function") { - initialValue = initialValue(); - } - - this.makeArgs(initialValue); + const initial = + typeof initialValue === "function" + ? (initialValue as () => T)() + : initialValue; + this.makeArgs(initial); } - update(): readonly [T, StateUpdater] { + update(): StateTuple { return this.args; } updater(value: NewState): void { const [previousValue] = this.args; if (typeof value === "function") { - const updaterFn = value as (previousState?: T) => T; + const updaterFn = value as (previousState: T) => T; value = updaterFn(previousValue); } @@ -45,14 +51,9 @@ const useState = hook( } makeArgs(value: T): void { - this.args = Object.freeze([value, this.updater] as const); + this.args = Object.freeze([value, this.updater]); } } -) as ( - initialValue?: T -) => readonly [ - T extends (...args: any[]) => infer R ? R : T, - StateUpdater infer S ? S : T> -]; +) as UseState; export { useState }; From cb123fd6082f0c0eb08b858d363c05579d848228 Mon Sep 17 00:00:00 2001 From: Meghea Iulian Date: Fri, 26 Jul 2024 17:15:09 +0300 Subject: [PATCH 2/5] Improve the type definition of `useProperty`. --- src/use-property.ts | 31 +++++++++++++++++-------------- src/use-state.ts | 12 +++++++----- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/use-property.ts b/src/use-property.ts index 056bc62..d26f4bb 100644 --- a/src/use-property.ts +++ b/src/use-property.ts @@ -1,18 +1,27 @@ import { hook, Hook } from "./hook"; import { State } from "./state"; +import type { + InitialState, + NewState, + StateUpdater, + StateTuple, +} from "./use-state"; type Host = Element & { [key: string]: T }; -type NewState = T | ((previousState?: T) => T); -type StateUpdater = (value: NewState) => void; type ChangeEvent = { value: T; path: string; }; +export interface UseProperty { + (property: string): StateTuple; + (property: string, value?: InitialState): StateTuple; +} + const UPPER = /([A-Z])/gu; export const useProperty = hook( - class extends Hook<[string, T], [T, StateUpdater], Host> { + class extends Hook<[string, T], StateTuple, Host> { property: string; eventName: string; @@ -20,7 +29,7 @@ export const useProperty = hook( id: number, state: State>, property: string, - initialValue: NewState + initialValue: InitialState ) { super(id, state); @@ -46,7 +55,7 @@ export const useProperty = hook( this.updateProp(initialValue); } - update(ignored: string, ignored2: T): [T, StateUpdater] { + update(ignored: string, ignored2: T): StateTuple { return [this.state.host[this.property], this.updater]; } @@ -54,7 +63,7 @@ export const useProperty = hook( const previousValue = this.state.host[this.property]; if (typeof value === "function") { - const updaterFn = value as (previousState?: T) => T; + const updaterFn = value as (previousState: T) => T; value = updaterFn(previousValue); } @@ -80,16 +89,10 @@ export const useProperty = hook( return ev; } } -) as ( - property: string, - initialValue?: T -) => readonly [ - T extends (...args: any[]) => infer R ? R : T, - StateUpdater infer S ? S : T> -]; +) as UseProperty; export const lift = - (setter: (value: T) => void) => + (setter: StateUpdater) => (ev: CustomEvent>) => { ev.preventDefault(); setter(ev.detail.value); diff --git a/src/use-state.ts b/src/use-state.ts index ed37423..cce467f 100644 --- a/src/use-state.ts +++ b/src/use-state.ts @@ -24,11 +24,13 @@ const useState = hook( constructor(id: number, state: State, initialValue: InitialState) { super(id, state); this.updater = this.updater.bind(this); - const initial = - typeof initialValue === "function" - ? (initialValue as () => T)() - : initialValue; - this.makeArgs(initial); + + if (typeof initialValue === "function") { + const initFn = initialValue as () => T; + initialValue = initFn(); + } + + this.makeArgs(initialValue); } update(): StateTuple { From 0e5d30d1e4e335dd1f9f265eb00b070a5e5391a6 Mon Sep 17 00:00:00 2001 From: Meghea Iulian Date: Fri, 26 Jul 2024 17:16:51 +0300 Subject: [PATCH 3/5] Cleanup exported types --- src/core.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/core.ts b/src/core.ts index 19b7510..d1be004 100644 --- a/src/core.ts +++ b/src/core.ts @@ -46,7 +46,7 @@ export { export { useCallback } from "./use-callback"; export { useEffect } from "./use-effect"; export { useLayoutEffect } from "./use-layout-effect"; -export { useState } from "./use-state"; +export { useState, StateUpdater } from "./use-state"; export { useReducer } from "./use-reducer"; export { useMemo } from "./use-memo"; export { useContext } from "./use-context"; @@ -58,11 +58,3 @@ export { State } from "./state"; export type { Ref } from "./use-ref"; export type { Options as ComponentOptions } from "./component"; - -export type { - StateUpdater, - InitialState, - NewState, - StateTuple, - UseState, -} from "./use-state.ts"; From e705e82c9f3659aad03fec484b626053c3c3582f Mon Sep 17 00:00:00 2001 From: Meghea Iulian Date: Fri, 26 Jul 2024 17:18:33 +0300 Subject: [PATCH 4/5] Describe changes --- .changeset/great-wasps-juggle.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/great-wasps-juggle.md diff --git a/.changeset/great-wasps-juggle.md b/.changeset/great-wasps-juggle.md new file mode 100644 index 0000000..d3db314 --- /dev/null +++ b/.changeset/great-wasps-juggle.md @@ -0,0 +1,6 @@ +--- +"@pionjs/pion": minor +--- + +Improve the type definition of useState and useProperty adding better support for initial values +and return better defined state updaters. From d3b0e2c7d95da3215594cbc07de8e373fe020a8e Mon Sep 17 00:00:00 2001 From: Meghea Iulian Date: Fri, 26 Jul 2024 17:24:10 +0300 Subject: [PATCH 5/5] Correct type export --- src/core.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core.ts b/src/core.ts index d1be004..637f9f5 100644 --- a/src/core.ts +++ b/src/core.ts @@ -46,7 +46,7 @@ export { export { useCallback } from "./use-callback"; export { useEffect } from "./use-effect"; export { useLayoutEffect } from "./use-layout-effect"; -export { useState, StateUpdater } from "./use-state"; +export { useState } from "./use-state"; export { useReducer } from "./use-reducer"; export { useMemo } from "./use-memo"; export { useContext } from "./use-context"; @@ -58,3 +58,5 @@ export { State } from "./state"; export type { Ref } from "./use-ref"; export type { Options as ComponentOptions } from "./component"; + +export type { StateUpdater } from "./use-state";