jakobhellermann / bevy_mod_js_scripting

Other
83 stars 6 forks source link

Export TypeScript Definitions For Game Components #15

Open zicklag opened 2 years ago

zicklag commented 2 years ago

It should be possible for us to export TypeScript definitions for all of the types that we can find in the Bevy type registry. That would allow us to give type definitions for all the components you can interact with in scripts.

We may want to come up with a way that you could import the type definition for Vec3 for instance, and then it could contain the full type name in it so that you could use it to extract the ComponentInfo and ComponentId for that type.

This needs some thought, but I think we should be able to make a nice workflow.

jakobhellermann commented 2 years ago

Maybe they could be autogenerated using the static TypeInfo if available https://docs.rs/bevy_reflect/latest/bevy_reflect/enum.TypeInfo.html

zicklag commented 2 years ago

Yeah, that was what I was thinking. And if anything is dynamic it would just map to any.

jakobhellermann commented 2 years ago

It's not possible to associate values with types in typescript, right?

It would be cool if

type Transform = {
  translation: Vec3;
  // ???
};

console.log(Transform.type_name) // the type name we can look up in the type registry 

would work somehow.

jakobhellermann commented 2 years ago

Like an associated constant in rust

zicklag commented 2 years ago

Yeah, unfortunately the closest thing I can find so far is a class with static field:

class Velocity {
  static typeName: string = "breakout::Velocity";
  0: Vec3
}

class Vec3 {
  static typeName: string = "bevy::math::Vec3";
  x: number;
  y: number;
}

This works, but unfortunately because it's an actual class and not done at TypeScript compile time, it requires that we actually import the generated bindings. We can't put them in an "ambient context" like the rest of the types in lib.bevy.d.ts.

That might require that we find a way to mange manage imports in scripts, but I did want to do that anyway, and I don't think it's going to be incredibly difficult, now that I got export rewriting working in #11.

jakobhellermann commented 2 years ago

That alone can't make

world.getResource<Time>();

work, right? Because we have no instance of the type available.

Maybe

function getResource<T>(ty: BevyType<T>) {}

interface Time {
  delta_seconds: number;
}
const Time: BevyType<Time> = { typeName: "..." };

let time = world.getResource(Time);
// Time type is inferred 

I don't know if typescripts type inference is good enough for that, and if it has separate type and value namespaces.

jakobhellermann commented 2 years ago

Update: typescripts inference is strong enough. This works:

type BevyType<T> = { typeName: string; };

type Time = {
    seconds: number,
};
const Time: BevyType<Time> = { typeName: "bevy_core::time::Time" };

function getResource<T>(type: BevyType<T>): T {
    throw new Error();
}

let resource = getResource(Time);
resource.seconds;
zicklag commented 2 years ago

Oh sweet! That's great.

jakobhellermann commented 2 years ago

Exporting all types in a quick and hacky way is pretty easy: https://github.com/jakobhellermann/bevy_reflect_ts_type_export/blob/main/generated/types.ts

And working with typed APIs is super nice: https://github.com/jakobhellermann/bevy_mod_js_scripting/blob/5b2df5e6db55eeb1fade17b56e05d303326d5e9e/assets/scripts/breakout.ts#L12-L23

jakobhellermann commented 2 years ago

TS can just overload two method signatures, and the deno op can deal with either. https://github.com/jakobhellermann/bevy_mod_js_scripting/blob/5b2df5e6db55eeb1fade17b56e05d303326d5e9e/src/runtime/js/index.d.ts#L53-L54

zicklag commented 2 years ago

Great, now we just have to set it up so you can import { Vec3 } from "./types.ts in a script so that you don't have to copy-and-paste the components from types.ts.

I think I'll work on that after finishing #11, which is getting pretty close to working now.