Skip to content

Commit 42cc556

Browse files
authored
Merge pull request #9 from tapava/feature/typed-registry
feat: add typed registry for type-safe function names and signatures
2 parents 1025667 + 13d2cd2 commit 42cc556

7 files changed

Lines changed: 594 additions & 71 deletions

File tree

README.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
- 🚀 **Non-blocking** : Everything runs in Web Workers
2929
- 🔧 **Zero config** : No manual worker files or postMessage handlers
3030
- 📦 **Tiny** : Core library is ~5KB gzipped
31-
- 🎯 **TypeScript** : Full type safety for your compute functions and WASM bindings
31+
- 🎯 **TypeScript** : Full type safety with [typed registry](#-typed-registry) for autocomplete and compile-time checks
3232
- 📊 **Progress tracking** : Built-in progress reporting for long-running tasks
3333

3434
---
@@ -313,6 +313,53 @@ const { progress, run } = useCompute('longTask', {
313313

314314
---
315315

316+
## 🏷️ Typed Registry
317+
318+
Get **autocomplete** and **type safety** for your compute functions by extending the `ComputeFunctionRegistry` interface:
319+
320+
```typescript
321+
// Extend the registry (in a .d.ts file or at the top of your file)
322+
declare module '@computekit/core' {
323+
interface ComputeFunctionRegistry {
324+
fibonacci: { input: number; output: number };
325+
sum: { input: number[]; output: number };
326+
}
327+
}
328+
```
329+
330+
Now you get full type inference:
331+
332+
```typescript
333+
// ✅ Types are inferred - no need for generics!
334+
kit.register('fibonacci', (n) => {
335+
// n is inferred as number
336+
if (n <= 1) return n;
337+
let a = 0,
338+
b = 1;
339+
for (let i = 2; i <= n; i++) {
340+
[a, b] = [b, a + b];
341+
}
342+
return b;
343+
});
344+
345+
const result = await kit.run('fibonacci', 50); // result is number
346+
347+
// ❌ TypeScript error: Argument of type 'string' is not assignable
348+
await kit.run('fibonacci', 'not a number');
349+
```
350+
351+
Works with React hooks too:
352+
353+
```tsx
354+
// Types inferred from registry
355+
const { data, run } = useCompute('fibonacci');
356+
// data: number | null, run: (n: number) => void
357+
```
358+
359+
See the [API Reference](https://tapava.github.io/compute-kit/api-reference#typed-registry) for more details.
360+
361+
---
362+
316363
## 📖 API
317364

318365
### `ComputeKit`

docs/api-reference.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,122 @@ new ComputeKit(options?: ComputeKitOptions)
4646

4747
---
4848

49+
## Typed Registry
50+
51+
ComputeKit supports **type-safe function registration** using TypeScript's declaration merging. This provides:
52+
53+
- **Autocomplete** for registered function names
54+
- **Type inference** for input and output types
55+
- **Compile-time errors** for incorrect usage
56+
57+
### Setting Up the Typed Registry
58+
59+
Extend the `ComputeFunctionRegistry` interface to define your function signatures:
60+
61+
```typescript
62+
// In a .d.ts file or at the top of your main file
63+
declare module '@computekit/core' {
64+
interface ComputeFunctionRegistry {
65+
fibonacci: { input: number; output: number };
66+
sum: { input: number[]; output: number };
67+
processData: { input: { items: string[] }; output: { count: number } };
68+
}
69+
}
70+
```
71+
72+
### Using DefineFunction Helper
73+
74+
For cleaner syntax, use the `DefineFunction` type helper:
75+
76+
```typescript
77+
import type { DefineFunction } from '@computekit/core';
78+
79+
declare module '@computekit/core' {
80+
interface ComputeFunctionRegistry {
81+
fibonacci: DefineFunction<number, number>;
82+
sum: DefineFunction<number[], number>;
83+
processData: DefineFunction<{ items: string[] }, { count: number }>;
84+
}
85+
}
86+
```
87+
88+
### Benefits
89+
90+
Once you've extended the registry, you get full type safety:
91+
92+
```typescript
93+
import { ComputeKit } from '@computekit/core';
94+
95+
const kit = new ComputeKit();
96+
97+
// ✅ Types are inferred - no need for generics!
98+
kit.register('fibonacci', (n) => {
99+
// n is inferred as number
100+
if (n <= 1) return n;
101+
let a = 0,
102+
b = 1;
103+
for (let i = 2; i <= n; i++) {
104+
[a, b] = [b, a + b];
105+
}
106+
return b; // return type must be number
107+
});
108+
109+
// ✅ Input type is enforced
110+
const result = await kit.run('fibonacci', 50); // result is number
111+
112+
// ❌ TypeScript error: 'unknownFunc' is not in the registry
113+
await kit.run('unknownFunc', 42);
114+
115+
// ❌ TypeScript error: Argument of type 'string' is not assignable to 'number'
116+
await kit.run('fibonacci', 'not a number');
117+
```
118+
119+
### With React Hooks
120+
121+
The typed registry works seamlessly with React hooks:
122+
123+
```tsx
124+
import { useCompute, useComputeCallback } from '@computekit/react';
125+
126+
function FibonacciCalculator() {
127+
// ✅ Types are inferred from the registry
128+
const { data, loading, run } = useCompute('fibonacci');
129+
// data is number | null
130+
// run expects (input: number) => void
131+
132+
return (
133+
<button onClick={() => run(50)}>
134+
{loading ? 'Computing...' : `Result: ${data}`}
135+
</button>
136+
);
137+
}
138+
```
139+
140+
### Registry Type Helpers
141+
142+
| Type | Description |
143+
| ------------------------- | ---------------------------------------------------------- |
144+
| `ComputeFunctionRegistry` | The base interface to extend with your functions |
145+
| `RegisteredFunctionName` | Union of all registered function names |
146+
| `FunctionInput<Name>` | Get the input type for a function |
147+
| `FunctionOutput<Name>` | Get the output type for a function |
148+
| `DefineFunction<I, O>` | Helper to define `{ input: I; output: O }` |
149+
| `ComputeFn<I, O>` | Type for compute functions `(input: I) => O \| Promise<O>` |
150+
| `InferComputeFn<Name>` | Infer the full function type from a name |
151+
| `HasRegisteredFunctions` | Boolean type indicating if registry has entries |
152+
153+
### Fallback Behavior
154+
155+
If you don't extend the registry, ComputeKit falls back to the original behavior with explicit generics:
156+
157+
```typescript
158+
// Still works without registry extension
159+
const { data, run } = useCompute<number, number>('fibonacci');
160+
const result = await kit.run<number, number>('fibonacci', 50);
161+
```
162+
163+
---
164+
49165
## Methods
50166

51167
### initialize()

examples/react-demo/src/demos/ParallelBlurDemo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,12 @@ export function ParallelBlurDemo() {
168168

169169
const promises = sourceImages.map(async (img, index) => {
170170
const start = performance.now();
171-
const result = await kit.run<BlurInput, number[]>('blurImage', {
171+
const result = (await kit.run('blurImage', {
172172
imageData: Array.from(img),
173173
width: imageSize,
174174
height: imageSize,
175175
passes: blurPasses,
176-
});
176+
})) as number[];
177177
const time = performance.now() - start;
178178
return {
179179
id: index,

0 commit comments

Comments
 (0)