Skip to content

Commit b81c8ec

Browse files
authored
Merge pull request #489 from matthewp/feat/update-types
QoL improvements to types
2 parents ec442cd + 8ff7db9 commit b81c8ec

File tree

10 files changed

+94
-40
lines changed

10 files changed

+94
-40
lines changed

.changeset/khaki-actors-work.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"haunted": minor
3+
---
4+
5+
Export type `Options` from component as `ComponentOptions`.

.changeset/major-coats-march.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"haunted": minor
3+
---
4+
5+
Allow omitting initialValue from useRef.

.changeset/quiet-mammals-look.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"haunted": minor
3+
---
4+
5+
Infer `virtual` type definition from renderer arguments.

.changeset/red-regions-shout.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"haunted": minor
3+
---
4+
5+
export Ref type

.changeset/salty-teams-nail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"haunted": minor
3+
---
4+
5+
Improve the defintion of useState, better handling initial value.

src/component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ interface Creator {
2626
): Constructor<P>;
2727
}
2828

29-
interface Options<P> {
29+
export interface Options<P> {
3030
baseElement?: Constructor<{}>;
3131
observedAttributes?: (keyof P)[];
3232
useShadowDOM?: boolean;

src/core.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,6 @@ export { useRef } from "./use-ref";
5151
export { hook, Hook } from "./hook";
5252
export { BaseScheduler } from "./scheduler";
5353
export { State } from "./state";
54+
export type { Ref } from "./use-ref";
55+
export type { Options as ComponentOptions } from "./component";
56+
export type { StateUpdater } from "./use-state";

src/use-ref.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import { useMemo } from "./use-memo";
22

3+
export interface Ref<T> {
4+
current: T;
5+
}
6+
37
/**
48
* @function
59
* @template T
610
* @param {T} initialValue
711
* @return {{ current: T }} Ref
812
*/
9-
const useRef = <T>(initialValue: T) =>
10-
useMemo(
13+
export function useRef<T>(): Ref<T | undefined>;
14+
export function useRef<T>(initialValue: T): Ref<T>;
15+
export function useRef<T>(initialValue?: T): Ref<T | undefined> {
16+
return useMemo(
1117
() => ({
1218
current: initialValue,
1319
}),
1420
[]
1521
);
16-
17-
export { useRef };
22+
}

src/use-state.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,46 @@
11
import { hook, Hook } from "./hook";
22
import { State } from "./state";
33

4-
type NewState<T> = T | ((previousState?: T) => T);
5-
type StateUpdater<T> = (value: NewState<T>) => void;
4+
export type InitialState<T> = T | (() => T);
5+
export type NewState<T> = T | ((previousState: T) => T);
6+
export type StateUpdater<T> = (value: NewState<T>) => void;
7+
export type StateTuple<T> = readonly [T, StateUpdater<T>];
8+
9+
export interface UseState {
10+
<T>(): StateTuple<T | undefined>;
11+
<T>(value?: InitialState<T>): StateTuple<T>;
12+
}
613

714
/**
815
* @function
916
* @template {*} T
1017
* @param {T} [initialState] - Optional initial state
11-
* @return {readonly [state: T, updaterFn: StateUpdater<T>]} stateTuple - Tuple of current state and state updater function
18+
* @return {StateTuple<T>} stateTuple - Tuple of current state and state updater function
1219
*/
1320
const useState = hook(
1421
class<T> extends Hook {
15-
args!: readonly [T, StateUpdater<T>];
22+
args!: StateTuple<T>;
1623

17-
constructor(id: number, state: State, initialValue: T) {
24+
constructor(id: number, state: State, initialValue: InitialState<T>) {
1825
super(id, state);
1926
this.updater = this.updater.bind(this);
2027

2128
if (typeof initialValue === "function") {
22-
initialValue = initialValue();
29+
const initFn = initialValue as () => T;
30+
initialValue = initFn();
2331
}
2432

2533
this.makeArgs(initialValue);
2634
}
2735

28-
update(): readonly [T, StateUpdater<T>] {
36+
update(): StateTuple<T> {
2937
return this.args;
3038
}
3139

3240
updater(value: NewState<T>): void {
3341
const [previousValue] = this.args;
3442
if (typeof value === "function") {
35-
const updaterFn = value as (previousState?: T) => T;
43+
const updaterFn = value as (previousState: T) => T;
3644
value = updaterFn(previousValue);
3745
}
3846

@@ -45,14 +53,9 @@ const useState = hook(
4553
}
4654

4755
makeArgs(value: T): void {
48-
this.args = Object.freeze([value, this.updater] as const);
56+
this.args = Object.freeze([value, this.updater]);
4957
}
5058
}
51-
) as <T>(
52-
initialValue?: T
53-
) => readonly [
54-
T extends (...args: any[]) => infer R ? R : T,
55-
StateUpdater<T extends (...args: any[]) => infer S ? S : T>
56-
];
59+
) as UseState;
5760

5861
export { useState };

src/virtual.ts

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,31 @@ import {
33
DirectiveParameters,
44
ChildPart,
55
PartInfo,
6-
} from "lit-html/directive.js";
7-
import { noChange } from "lit-html";
8-
import { AsyncDirective } from "lit-html/async-directive.js";
6+
} from "lit/directive.js";
7+
import { noChange } from "lit";
8+
import { AsyncDirective } from "lit/async-directive.js";
99
import { GenericRenderer } from "./core";
1010
import { BaseScheduler } from "./scheduler";
1111

1212
const includes = Array.prototype.includes;
1313

14-
interface Renderer extends GenericRenderer<ChildPart> {
15-
(this: ChildPart, ...args: unknown[]): unknown | void;
14+
interface Renderer<T extends unknown[]> extends GenericRenderer<ChildPart> {
15+
(this: ChildPart, ...args: T): unknown | void;
1616
}
1717

18-
const partToScheduler: WeakMap<ChildPart, Scheduler> = new WeakMap();
19-
const schedulerToPart: WeakMap<Scheduler, ChildPart> = new WeakMap();
18+
const partToScheduler: WeakMap<ChildPart, Scheduler<any>> = new WeakMap();
19+
const schedulerToPart: WeakMap<Scheduler<any>, ChildPart> = new WeakMap();
2020

21-
class Scheduler extends BaseScheduler<object, ChildPart, Renderer, ChildPart> {
22-
args!: unknown[];
21+
class Scheduler<T extends unknown[]> extends BaseScheduler<
22+
object,
23+
ChildPart,
24+
Renderer<T>,
25+
ChildPart
26+
> {
27+
args!: T;
2328
setValue: Function;
2429

25-
constructor(renderer: Renderer, part: ChildPart, setValue: Function) {
30+
constructor(renderer: Renderer<T>, part: ChildPart, setValue: Function) {
2631
super(renderer, part);
2732
this.state.virtual = true;
2833
this.setValue = setValue;
@@ -43,10 +48,19 @@ class Scheduler extends BaseScheduler<object, ChildPart, Renderer, ChildPart> {
4348
}
4449
}
4550

46-
function makeVirtual(): any {
47-
function virtual(renderer: Renderer) {
51+
interface VirtualRenderer<T extends unknown[]> {
52+
(this: ChildPart, ...args: T): unknown | void;
53+
}
54+
export interface Virtual {
55+
<T extends unknown[]>(renderer: VirtualRenderer<T>): (
56+
...values: T
57+
) => unknown;
58+
}
59+
60+
function makeVirtual(): Virtual {
61+
function virtual<T extends unknown[]>(renderer: VirtualRenderer<T>) {
4862
class VirtualDirective extends AsyncDirective {
49-
cont: Scheduler | undefined;
63+
cont: Scheduler<T> | undefined;
5064

5165
constructor(partInfo: PartInfo) {
5266
super(partInfo);
@@ -56,19 +70,23 @@ function makeVirtual(): any {
5670
update(part: ChildPart, args: DirectiveParameters<this>) {
5771
this.cont = partToScheduler.get(part);
5872
if (!this.cont || this.cont.renderer !== renderer) {
59-
this.cont = new Scheduler(renderer, part, (r: unknown) => {
60-
this.setValue(r);
61-
});
73+
this.cont = new Scheduler(
74+
renderer as Renderer<T>,
75+
part,
76+
(r: unknown) => {
77+
this.setValue(r);
78+
}
79+
);
6280
partToScheduler.set(part, this.cont);
6381
schedulerToPart.set(this.cont, part);
6482
teardownOnRemove(this.cont, part);
6583
}
6684
this.cont.args = args;
6785
this.cont.update();
68-
return this.render(args);
86+
return this.render(...args);
6987
}
7088

71-
render(args: unknown) {
89+
render(...args: T) {
7290
return noChange;
7391
}
7492
}
@@ -79,8 +97,8 @@ function makeVirtual(): any {
7997
return virtual;
8098
}
8199

82-
function teardownOnRemove(
83-
cont: BaseScheduler<object, ChildPart, Renderer, ChildPart>,
100+
function teardownOnRemove<T extends unknown[]>(
101+
cont: BaseScheduler<object, ChildPart, Renderer<T>, ChildPart>,
84102
part: ChildPart,
85103
node = part.startNode
86104
): void {

0 commit comments

Comments
 (0)