From ddb2dead081d85747b03e6a56de3c321f9c67f23 Mon Sep 17 00:00:00 2001 From: Will Eastcott Date: Sun, 13 Apr 2025 21:09:45 +0100 Subject: [PATCH 1/3] Tighten up types for Entity API --- src/framework/entity.js | 52 ++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/src/framework/entity.js b/src/framework/entity.js index 076690906ca..2c76b30d6fa 100644 --- a/src/framework/entity.js +++ b/src/framework/entity.js @@ -30,6 +30,32 @@ import { getApplication } from './globals.js'; * @import { SpriteComponent } from './components/sprite/component.js' */ +/** + * @typedef {{ + * anim: AnimComponent; + * animation: AnimationComponent; + * audiolistener: AudioListenerComponent; + * button: ButtonComponent; + * camera: CameraComponent; + * collision: CollisionComponent; + * element: ElementComponent; + * gsplat: GSplatComponent; + * layoutchild: LayoutChildComponent; + * layoutgroup: LayoutGroupComponent; + * light: LightComponent; + * model: ModelComponent; + * particlesystem: ParticleSystemComponent; + * render: RenderComponent; + * rigidbody: RigidBodyComponent; + * screen: ScreenComponent; + * script: ScriptComponent; + * scrollbar: ScrollbarComponent; + * scrollview: ScrollViewComponent; + * sound: SoundComponent; + * sprite: SpriteComponent; + * }} ComponentMap A map of component names to their class types. + */ + /** * @param {Component} a - First object with `order` property. * @param {Component} b - Second object with `order` property. @@ -339,7 +365,8 @@ class Entity extends GraphNode { * Create a new component and add it to the entity. Use this to add functionality to the entity * like rendering a model, playing sounds and so on. * - * @param {string} type - The name of the component to add. Valid strings are: + * @template {keyof ComponentMap} T + * @param {T} type - The name of the component to add. Valid strings are: * * - "anim" - see {@link AnimComponent} * - "animation" - see {@link AnimationComponent} @@ -363,10 +390,11 @@ class Entity extends GraphNode { * - "sound" - see {@link SoundComponent} * - "sprite" - see {@link SpriteComponent} * - * @param {object} [data] - The initialization data for the specific component type. Refer to - * each specific component's API reference page for details on valid values for this parameter. - * @returns {Component|null} The new Component that was attached to the entity or null if there - * was an error. + * @param {Partial} [data] - The initialization data for the specific + * component type. Refer to each specific component's API reference page for details on valid + * values for this parameter. + * @returns {ComponentMap[T] | null} The new Component that was attached to the entity or null + * if there was an error. * @example * const entity = new pc.Entity(); * @@ -395,7 +423,7 @@ class Entity extends GraphNode { /** * Remove a component from the Entity. * - * @param {string} type - The name of the Component type. + * @param {keyof ComponentMap} type - The name of the Component type. * @example * const entity = new pc.Entity(); * entity.addComponent("light"); // add new light component @@ -418,9 +446,10 @@ class Entity extends GraphNode { /** * Search the entity and all of its descendants for the first component of specified type. * - * @param {string} type - The name of the component type to retrieve. - * @returns {Component} A component of specified type, if the entity or any of its descendants - * has one. Returns undefined otherwise. + * @template {keyof ComponentMap} T + * @param {T} type - The name of the component type to retrieve. + * @returns {ComponentMap[T] | undefined} A component of specified type, if the entity or any + * of its descendants has one. Returns undefined otherwise. * @example * // Get the first found light component in the hierarchy tree that starts with this entity * const light = entity.findComponent("light"); @@ -433,8 +462,9 @@ class Entity extends GraphNode { /** * Search the entity and all of its descendants for all components of specified type. * - * @param {string} type - The name of the component type to retrieve. - * @returns {Component[]} All components of specified type in the entity or any of its + * @template {keyof ComponentMap} T + * @param {T} type - The name of the component type to retrieve. + * @returns {ComponentMap[T][]} All components of specified type in the entity or any of its * descendants. Returns empty array if none found. * @example * // Get all light components in the hierarchy tree that starts with this entity From ae2cbdf932eb3b50c9e9720806294176e41dfb4e Mon Sep 17 00:00:00 2001 From: Will Eastcott Date: Mon, 14 Apr 2025 10:53:40 +0100 Subject: [PATCH 2/3] Improve typedef description --- src/framework/entity.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/entity.js b/src/framework/entity.js index 2c76b30d6fa..463d3d17581 100644 --- a/src/framework/entity.js +++ b/src/framework/entity.js @@ -53,7 +53,8 @@ import { getApplication } from './globals.js'; * scrollview: ScrollViewComponent; * sound: SoundComponent; * sprite: SpriteComponent; - * }} ComponentMap A map of component names to their class types. + * }} ComponentMap A map of component names to their class types. Facilitates typings for + * {@link Entity#addComponent}. */ /** From 892b200c52816eeef2efdb2e5916c08d3476800c Mon Sep 17 00:00:00 2001 From: Will Eastcott Date: Mon, 14 Apr 2025 19:43:08 +0100 Subject: [PATCH 3/3] Add support for custom components --- src/framework/entity.js | 88 +++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/src/framework/entity.js b/src/framework/entity.js index 463d3d17581..f868bca5f80 100644 --- a/src/framework/entity.js +++ b/src/framework/entity.js @@ -363,51 +363,71 @@ class Entity extends GraphNode { } /** - * Create a new component and add it to the entity. Use this to add functionality to the entity - * like rendering a model, playing sounds and so on. + * Add a built-in component to the entity. The associated component system must be registered + * with the application. * * @template {keyof ComponentMap} T - * @param {T} type - The name of the component to add. Valid strings are: - * - * - "anim" - see {@link AnimComponent} - * - "animation" - see {@link AnimationComponent} - * - "audiolistener" - see {@link AudioListenerComponent} - * - "button" - see {@link ButtonComponent} - * - "camera" - see {@link CameraComponent} - * - "collision" - see {@link CollisionComponent} - * - "element" - see {@link ElementComponent} - * - "gsplat" - see {@link GSplatComponent} - * - "layoutchild" - see {@link LayoutChildComponent} - * - "layoutgroup" - see {@link LayoutGroupComponent} - * - "light" - see {@link LightComponent} - * - "model" - see {@link ModelComponent} - * - "particlesystem" - see {@link ParticleSystemComponent} - * - "render" - see {@link RenderComponent} - * - "rigidbody" - see {@link RigidBodyComponent} - * - "screen" - see {@link ScreenComponent} - * - "script" - see {@link ScriptComponent} - * - "scrollbar" - see {@link ScrollbarComponent} - * - "scrollview" - see {@link ScrollViewComponent} - * - "sound" - see {@link SoundComponent} - * - "sprite" - see {@link SpriteComponent} - * - * @param {Partial} [data] - The initialization data for the specific - * component type. Refer to each specific component's API reference page for details on valid - * values for this parameter. - * @returns {ComponentMap[T] | null} The new Component that was attached to the entity or null - * if there was an error. + * @overload + * @param {T} type - The type of built-in component to add. Can be: + * - 'anim' - see {@link AnimComponent} + * - 'animation' - see {@link AnimationComponent} + * - 'audiolistener' - see {@link AudioListenerComponent} + * - 'button' - see {@link ButtonComponent} + * - 'camera' - see {@link CameraComponent} + * - 'collision' - see {@link CollisionComponent} + * - 'element' - see {@link ElementComponent} + * - 'gsplat' - see {@link GSplatComponent} + * - 'layoutchild' - see {@link LayoutChildComponent} + * - 'layoutgroup' - see {@link LayoutGroupComponent} + * - 'light' - see {@link LightComponent} + * - 'model' - see {@link ModelComponent} + * - 'particlesystem' - see {@link ParticleSystemComponent} + * - 'render' - see {@link RenderComponent} + * - 'rigidbody' - see {@link RigidBodyComponent} + * - 'screen' - see {@link ScreenComponent} + * - 'script' - see {@link ScriptComponent} + * - 'scrollbar' - see {@link ScrollbarComponent} + * - 'scrollview' - see {@link ScrollViewComponent} + * - 'sound' - see {@link SoundComponent} + * - 'sprite' - see {@link SpriteComponent} + * @param {Partial} [data] - Optional initialization data for the component. + * Refer to each specific component's API reference page for details on valid values for this + * parameter. + * @returns {ComponentMap[T] | null} The newly added component or null in the case of an error. * @example * const entity = new pc.Entity(); * * // Add a light component with default properties - * entity.addComponent("light"); + * entity.addComponent('light'); * - * // Add a camera component with some specified properties - * entity.addComponent("camera", { + * // Add a camera component with some initialization data + * entity.addComponent('camera', { * fov: 45, * clearColor: new pc.Color(1, 0, 0) * }); */ + /** + * Add a custom component to the entity. The associated component system must be registered + * with the application. + * + * @overload + * @param {string} type - The type of custom component to add. + * @param {any} [data] - Optional initialization data for the component. + * @returns {Component | null} The newly added component or null in the case of an error. + * @example + * const entity = new pc.Entity(); + * + * // Add a custom component with some initialization data + * entity.addComponent('custom', { + * foo: 'bar' + * }); + */ + /** + * @template {keyof ComponentMap} T + * @param {T | string} type - The type of component to add. + * @param {Partial | any} [data] - Optional initialization data for the component. + * @returns {ComponentMap[T] | Component | null} The newly added component or null in the case of an error. + */ addComponent(type, data) { const system = this._app.systems[type]; if (!system) {