Open Dykam opened 5 years ago
I'm confused, is that not exactly what we're doing here? https://github.com/minecraft-addon-tools/minecraft-scripting-types/blob/master/packages/server/types/server_system_components.generated.d.ts
Sorry, I wasn't clear. Right now the result would be the same, but for someone to add two strongly typed overloads one needs to add both using a declare global
, my suggestion pretty much ammounts to reducing that to a single place, using an interface to map the string names to types. Which then can also be consumed in different places to produce similarly overloaded functions.
I was thinking of doing this, but for different reasons - to allow making custom functions that accept components.
declare interface ComponentTypeMap {
"minecraft:position": IPositionComponent;
// ...
}
getComponent<K extends keyof ComponentTypeMap>(entity: IEntityObject, componentIdentifier: K): ComponentTypeMap[K] | null;
Possibly more powerful even is that it allows something like this:
function foo<T extends ComponentTypeMap[keyof ComponentTypeMap]>(component: T): T {
return component;
}
Huh, this looks like an area of TypeScript I haven't played with before, but I'm keen to try it out, this sounds like a nice improvement.
The downside of this is that we need to create an interface that is not intended to be implemented by anything.
Those interfaces even exist in the default libraries of Typescript. People don't randomly implement interface, so that's fine.
And maybe one could actually write a function returning Partial<ComponentTypeMap>
with e.g. all components of an entity, so it might even be implemented.
I'm having mixed success with attempting this. At first glance everything seems to be fine when you open a file, then as soon as I made a change, TypeScript's language server would get very confused.
const system = server.registerSystem(0, 0);
const enum MyComponent {
Test = "test:test"
}
interface ITestComponent {
x: number;
}
declare interface ComponentTypeMap {
[MyComponent.Test]: ITestComponent
}
system.initialize = function() {
system.listenForEvent(ReceiveFromMinecraftServer.EntityCreated, onEntityCreated);
}
function onEntityCreated(entity: IEntityObject) {
const component: IAttackComponent = system.getComponent(entity, MinecraftComponent.Attack)
component.damage = [0, 1];
if (system.hasComponent(entity, MinecraftComponent.CollisionBox)) {
const test: ICollisionBoxComponent = system.getComponent(entity, MinecraftComponent.CollisionBox);
}
if (system.hasComponent(entity, MyComponent.Test)) {
const test2: ITestComponent = system.getComponent(entity, MyComponent.Test);
}
}
All of the MinecraftComponents are specified in the ComponentTypeMap, and IntelliSense is correctly merging the two declarations of ComponentTypeMap together, however when attempting to use the custom component it's refusing it see it as a valid parameter.
Further investigation required
I don't have the code you have I think, so I can't test myself, but I think you'll probably need to put the declaration in a separate file, and declare as follows:
declare "minecraft-scripting-types-server" {
declare interface ComponentTypeMap {
[MyComponent.Test]: ITestComponent
}
}
Otherwise you might just be declaring a new ComponentTypeMap, and typescript gets confused.
Nevermind, just wrap your current declare in an declare global {}
, and use a string key directly. That works for me. After that you can use the enum for accessing, but I'm not sure why it doesn't work in the definition.
Similar to how dom.d.ts uses an interface to map stringly typed names to actual elements: (warning, large file) https://github.com/Microsoft/TypeScript/blob/b830ef8/src/lib/dom.generated.d.ts#L4247
This can then be augmented using a global declaration: