Skip to content

Commit 038a6ac

Browse files
committed
feat(ecs/zipper): add more typing in order to strenghen types
1 parent 45ec899 commit 038a6ac

File tree

6 files changed

+36
-13
lines changed

6 files changed

+36
-13
lines changed

example/pong/src/collisions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { type ECSRegistry } from "@nanoforge/ecs";
1+
import { type Registry } from "@nanoforge/ecs";
22

33
import { Hitbox, Position } from "./components";
44

5-
export function checkCollisions(registry: ECSRegistry, entity: any) {
5+
export function checkCollisions(registry: Registry, entity: any) {
66
const entities = registry.getZipper([Hitbox, Position]);
77

88
const { x, y } = entity.Position;

example/pong/src/systems.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type Context } from "@nanoforge/common";
2-
import { type ECSRegistry } from "@nanoforge/ecs";
2+
import { type Registry } from "@nanoforge/ecs";
33
import { type InputLibrary } from "@nanoforge/input";
44
import { type SoundLibrary } from "@nanoforge/sound";
55

@@ -14,7 +14,7 @@ import {
1414
Velocity,
1515
} from "./components";
1616

17-
export function move(registry: ECSRegistry) {
17+
export function move(registry: Registry) {
1818
const entities = registry.getZipper([Bounce, Position, Velocity]);
1919

2020
entities.forEach((entity) => {
@@ -23,7 +23,7 @@ export function move(registry: ECSRegistry) {
2323
});
2424
}
2525

26-
export function bounce(registry: ECSRegistry, ctx: Context) {
26+
export function bounce(registry: Registry, ctx: Context) {
2727
const entities = registry.getZipper([Bounce, Position, Velocity]);
2828

2929
entities.forEach((entity) => {
@@ -39,7 +39,7 @@ export function bounce(registry: ECSRegistry, ctx: Context) {
3939
});
4040
}
4141

42-
export function controlPlayer(registry: ECSRegistry, ctx: Context) {
42+
export function controlPlayer(registry: Registry, ctx: Context) {
4343
const entities = registry.getZipper([Controller, Position, Hitbox, Velocity]);
4444

4545
entities.forEach((entity) => {
@@ -62,7 +62,7 @@ export function controlPlayer(registry: ECSRegistry, ctx: Context) {
6262
});
6363
}
6464

65-
export function drawCircle(registry: ECSRegistry) {
65+
export function drawCircle(registry: Registry) {
6666
const entities = registry.getZipper([CircleComponent, Position]);
6767

6868
entities.forEach((entity) => {
@@ -71,7 +71,7 @@ export function drawCircle(registry: ECSRegistry) {
7171
});
7272
}
7373

74-
export function moveRectangle(registry: ECSRegistry) {
74+
export function moveRectangle(registry: Registry) {
7575
const entities = registry.getZipper([RectangleComponent, Position, Hitbox]);
7676

7777
entities.forEach((entity) => {

packages/ecs/lib/libecs.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,18 @@ type Component = {name: string, [key: string]: any};
4343

4444
type System = (registry: Registry, ctx: any) => void;
4545

46+
type ComponentNames<T extends Component[]> = T extends [infer First, ...infer Rest] ? First["name"] | ComponentNames<Rest> : never;
47+
48+
type ComponentForName<T extends Component[], N extends string> = T extends [infer First, ...infer Rest] ? First["name"] extends N ? First : ComponentForName<Rest, N> : never;;
49+
50+
type ZipperResult<T extends Component[]> = { [K in ComponentNames<T>]: ComponentForName<T, K extends string ? K : never>; };;
51+
4652
export interface Registry extends ClassHandle {
4753
registerComponent(_0: Component): SparseArray;
4854
getComponentsConst(_0: Component): SparseArray;
4955
getComponents(_0: Component): SparseArray;
5056
spawnEntity(): Entity;
57+
getZipper<T extends Component[]>(_0: [...T]): ZipperResult<T>;
5158
killEntity(_0: Entity): void;
5259
clearEntities(): void;
5360
removeComponent(_0: Entity, _1: Component): void;
@@ -60,7 +67,6 @@ export interface Registry extends ClassHandle {
6067
getEntityComponent(_0: Entity, _1: Component): any | undefined;
6168
addComponent(_0: Entity, _1: Component): any | undefined;
6269
runSystems(_0: any): void;
63-
getZipper(_0: Component[]): any;
6470
}
6571

6672
interface EmbindModule {

packages/ecs/wasm/Registry.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,28 @@
1919

2020
namespace nfo {
2121
EMSCRIPTEN_DECLARE_VAL_TYPE(System);
22+
EMSCRIPTEN_DECLARE_VAL_TYPE(ComponentNames);
23+
EMSCRIPTEN_DECLARE_VAL_TYPE(ComponentForName);
24+
EMSCRIPTEN_DECLARE_VAL_TYPE(ZZ)
2225

2326
EMSCRIPTEN_BINDINGS(Registry)
2427
{
2528
emscripten::register_type<Component>("Component", "{name: string, [key: string]: any}");
2629
emscripten::register_type<System>("System", "(registry: Registry, ctx: any) => void");
27-
emscripten::register_type<ZipperInput>("Component[]");
30+
emscripten::register_type<ZipperInput>("[...T]");
31+
emscripten::register_type<ComponentNames>(
32+
"ComponentNames<T extends Component[]>", "T extends [infer First, ...infer Rest] ? First[\"name\"] | ComponentNames<Rest> : never"
33+
);
34+
emscripten::register_type<ComponentForName>(
35+
"ComponentForName<T extends Component[], N extends string>",
36+
"T extends [infer First, ...infer Rest] ? First[\"name\"] extends N ? First : ComponentForName<Rest, N> : never;"
37+
);
38+
39+
emscripten::register_type<ZZ>(
40+
"ZipperResult<T extends Component[]>", "{ [K in ComponentNames<T>]: ComponentForName<T, K extends string ? K : never>; };"
41+
);
42+
43+
emscripten::register_type<ZipperOutput>("ZipperResult<T>");
2844

2945
emscripten::class_<Registry>("Registry")
3046
.constructor()
@@ -55,7 +71,7 @@ namespace nfo {
5571
.function("runSystems", &Registry::run_systems)
5672
.function("removeSystem", &Registry::remove_system)
5773
.function("clearSystems", &Registry::clear_systems)
58-
.function("getZipper", &Registry::get_zipper)
74+
.function("getZipper<T extends Component[]>", &Registry::get_zipper)
5975
.function("maxEntities", &Registry::max_entities);
6076
}
6177
} // namespace nfo

packages/ecs/wasm/Registry.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ namespace nfo {
180180
return _next_entity;
181181
}
182182

183-
emscripten::val get_zipper(const ZipperInput &comps)
183+
ZipperOutput get_zipper(const ZipperInput &comps)
184184
{
185185
if (!comps.isArray())
186186
throw std::runtime_error("getZipper: need an array of comps as parameter");
@@ -207,7 +207,7 @@ namespace nfo {
207207
if (need_to_add)
208208
arr.set(idx, obj);
209209
}
210-
return arr;
210+
return ZipperOutput(arr);
211211
}
212212

213213
private:

packages/ecs/wasm/Utils.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
EMSCRIPTEN_DECLARE_VAL_TYPE(Component);
2323
EMSCRIPTEN_DECLARE_VAL_TYPE(ComponentArray);
2424
EMSCRIPTEN_DECLARE_VAL_TYPE(ZipperInput);
25+
EMSCRIPTEN_DECLARE_VAL_TYPE(ZipperOutput);
2526

2627
std::optional<std::string> json_to_str(const emscripten::val &c);
2728
std::optional<emscripten::val> get_js_member(const emscripten::val &c, const std::string &member);

0 commit comments

Comments
 (0)