Skip to content

Commit

Permalink
Merge pull request #489 from matthewp/feat/update-types
Browse files Browse the repository at this point in the history
QoL improvements to types
  • Loading branch information
megheaiulian authored Feb 21, 2025
2 parents ec442cd + 8ff7db9 commit b81c8ec
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 40 deletions.
5 changes: 5 additions & 0 deletions .changeset/khaki-actors-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"haunted": minor
---

Export type `Options` from component as `ComponentOptions`.
5 changes: 5 additions & 0 deletions .changeset/major-coats-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"haunted": minor
---

Allow omitting initialValue from useRef.
5 changes: 5 additions & 0 deletions .changeset/quiet-mammals-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"haunted": minor
---

Infer `virtual` type definition from renderer arguments.
5 changes: 5 additions & 0 deletions .changeset/red-regions-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"haunted": minor
---

export Ref type
5 changes: 5 additions & 0 deletions .changeset/salty-teams-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"haunted": minor
---

Improve the defintion of useState, better handling initial value.
2 changes: 1 addition & 1 deletion src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface Creator {
): Constructor<P>;
}

interface Options<P> {
export interface Options<P> {
baseElement?: Constructor<{}>;
observedAttributes?: (keyof P)[];
useShadowDOM?: boolean;
Expand Down
3 changes: 3 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@ export { useRef } from "./use-ref";
export { hook, Hook } from "./hook";
export { BaseScheduler } from "./scheduler";
export { State } from "./state";
export type { Ref } from "./use-ref";
export type { Options as ComponentOptions } from "./component";
export type { StateUpdater } from "./use-state";
13 changes: 9 additions & 4 deletions src/use-ref.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import { useMemo } from "./use-memo";

export interface Ref<T> {
current: T;
}

/**
* @function
* @template T
* @param {T} initialValue
* @return {{ current: T }} Ref
*/
const useRef = <T>(initialValue: T) =>
useMemo(
export function useRef<T>(): Ref<T | undefined>;
export function useRef<T>(initialValue: T): Ref<T>;
export function useRef<T>(initialValue?: T): Ref<T | undefined> {
return useMemo(
() => ({
current: initialValue,
}),
[]
);

export { useRef };
}
33 changes: 18 additions & 15 deletions src/use-state.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
import { hook, Hook } from "./hook";
import { State } from "./state";

type NewState<T> = T | ((previousState?: T) => T);
type StateUpdater<T> = (value: NewState<T>) => void;
export type InitialState<T> = T | (() => T);
export type NewState<T> = T | ((previousState: T) => T);
export type StateUpdater<T> = (value: NewState<T>) => void;
export type StateTuple<T> = readonly [T, StateUpdater<T>];

export interface UseState {
<T>(): StateTuple<T | undefined>;
<T>(value?: InitialState<T>): StateTuple<T>;
}

/**
* @function
* @template {*} T
* @param {T} [initialState] - Optional initial state
* @return {readonly [state: T, updaterFn: StateUpdater<T>]} stateTuple - Tuple of current state and state updater function
* @return {StateTuple<T>} stateTuple - Tuple of current state and state updater function
*/
const useState = hook(
class<T> extends Hook {
args!: readonly [T, StateUpdater<T>];
args!: StateTuple<T>;

constructor(id: number, state: State, initialValue: T) {
constructor(id: number, state: State, initialValue: InitialState<T>) {
super(id, state);
this.updater = this.updater.bind(this);

if (typeof initialValue === "function") {
initialValue = initialValue();
const initFn = initialValue as () => T;
initialValue = initFn();
}

this.makeArgs(initialValue);
}

update(): readonly [T, StateUpdater<T>] {
update(): StateTuple<T> {
return this.args;
}

updater(value: NewState<T>): 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);
}

Expand All @@ -45,14 +53,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 <T>(
initialValue?: T
) => readonly [
T extends (...args: any[]) => infer R ? R : T,
StateUpdater<T extends (...args: any[]) => infer S ? S : T>
];
) as UseState;

export { useState };
58 changes: 38 additions & 20 deletions src/virtual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,31 @@ import {
DirectiveParameters,
ChildPart,
PartInfo,
} from "lit-html/directive.js";
import { noChange } from "lit-html";
import { AsyncDirective } from "lit-html/async-directive.js";
} from "lit/directive.js";
import { noChange } from "lit";
import { AsyncDirective } from "lit/async-directive.js";
import { GenericRenderer } from "./core";
import { BaseScheduler } from "./scheduler";

const includes = Array.prototype.includes;

interface Renderer extends GenericRenderer<ChildPart> {
(this: ChildPart, ...args: unknown[]): unknown | void;
interface Renderer<T extends unknown[]> extends GenericRenderer<ChildPart> {
(this: ChildPart, ...args: T): unknown | void;
}

const partToScheduler: WeakMap<ChildPart, Scheduler> = new WeakMap();
const schedulerToPart: WeakMap<Scheduler, ChildPart> = new WeakMap();
const partToScheduler: WeakMap<ChildPart, Scheduler<any>> = new WeakMap();
const schedulerToPart: WeakMap<Scheduler<any>, ChildPart> = new WeakMap();

class Scheduler extends BaseScheduler<object, ChildPart, Renderer, ChildPart> {
args!: unknown[];
class Scheduler<T extends unknown[]> extends BaseScheduler<
object,
ChildPart,
Renderer<T>,
ChildPart
> {
args!: T;
setValue: Function;

constructor(renderer: Renderer, part: ChildPart, setValue: Function) {
constructor(renderer: Renderer<T>, part: ChildPart, setValue: Function) {
super(renderer, part);
this.state.virtual = true;
this.setValue = setValue;
Expand All @@ -43,10 +48,19 @@ class Scheduler extends BaseScheduler<object, ChildPart, Renderer, ChildPart> {
}
}

function makeVirtual(): any {
function virtual(renderer: Renderer) {
interface VirtualRenderer<T extends unknown[]> {
(this: ChildPart, ...args: T): unknown | void;
}
export interface Virtual {
<T extends unknown[]>(renderer: VirtualRenderer<T>): (
...values: T
) => unknown;
}

function makeVirtual(): Virtual {
function virtual<T extends unknown[]>(renderer: VirtualRenderer<T>) {
class VirtualDirective extends AsyncDirective {
cont: Scheduler | undefined;
cont: Scheduler<T> | undefined;

constructor(partInfo: PartInfo) {
super(partInfo);
Expand All @@ -56,19 +70,23 @@ function makeVirtual(): any {
update(part: ChildPart, args: DirectiveParameters<this>) {
this.cont = partToScheduler.get(part);
if (!this.cont || this.cont.renderer !== renderer) {
this.cont = new Scheduler(renderer, part, (r: unknown) => {
this.setValue(r);
});
this.cont = new Scheduler(
renderer as Renderer<T>,
part,
(r: unknown) => {
this.setValue(r);
}
);
partToScheduler.set(part, this.cont);
schedulerToPart.set(this.cont, part);
teardownOnRemove(this.cont, part);
}
this.cont.args = args;
this.cont.update();
return this.render(args);
return this.render(...args);
}

render(args: unknown) {
render(...args: T) {
return noChange;
}
}
Expand All @@ -79,8 +97,8 @@ function makeVirtual(): any {
return virtual;
}

function teardownOnRemove(
cont: BaseScheduler<object, ChildPart, Renderer, ChildPart>,
function teardownOnRemove<T extends unknown[]>(
cont: BaseScheduler<object, ChildPart, Renderer<T>, ChildPart>,
part: ChildPart,
node = part.startNode
): void {
Expand Down

0 comments on commit b81c8ec

Please sign in to comment.