Skip to main content

Reflection

Reflection provides runtime OOP metadata for the Oidis framework — scanning globalThis namespaces to discover classes, inspect hierarchies, check types, instantiate objects dynamically, and resolve @Partial mixins. It is the backbone of Oidis's runtime type system.

Package: io-oidis-commons

import { Reflection } from "@io-oidis-commons/.../Utils/Reflection.js";

Overview​

JavaScript has no built-in runtime reflection for class hierarchies beyond instanceof. Oidis Reflection fills this gap by:

  1. Scanning registered namespaces on globalThis (e.g., globalThis.Io.Oidis.*)
  2. Building an internal map of all discovered classes
  3. Providing methods to query class hierarchies, check types, and instantiate classes by name

Reflection operates as a singleton — one instance manages all class metadata for the entire application.

Singleton Pattern​

// Get the singleton instance
const reflection = Reflection.getInstance();

// Configure which namespaces to scan
Reflection.setInstanceNamespaces("Io.Oidis", "Io.Oidis.MyProject");

// Refresh the singleton (re-scan namespaces after new classes are registered)
Reflection.Refresh();

Namespace Configuration​

By default, Reflection scans "Io.Oidis". Projects that use custom namespaces must register them:

// In Loader.ts — tell Reflection about project namespaces
Reflection.setInstanceNamespaces("Io.Oidis", "Com.Customer.ProjectX");

Namespace Mappings​

For namespace aliasing (e.g., mapping short names to full paths):

Reflection.setNamespaceMappings(
{ from: "*.AuthManager", to: "Io.Oidis.Hub.Utils.AuthManager" },
{ from: "*.MyService", to: "Io.Oidis.MyProject.Utils.MyService" }
);

Mappings are used by NormalizeNamespace and class resolution methods.

Static Methods​

Class Identity​

Reflection.ClassName() : string                    // "Io.Oidis.Commons.Utils.Reflection"
Reflection.NamespaceName() : string // "Io.Oidis.Commons.Utils"
Reflection.ClassNameWithoutNamespace() : string // "Reflection"

Unique Identifiers​

Reflection.UID($className? : string) : string

Generates a unique identifier based on class name and timestamp:

const id = Reflection.UID();                // Based on Reflection's own name
const id = Reflection.UID("MyRecord"); // Based on "MyRecord"

Dynamic Instantiation​

Reflection.getInstanceOf($className : string, $dataObject? : object) : any

Creates a new instance of a class by its namespace string. Optionally passes a data object to the constructor:

// Instantiate a class by name
const record = Reflection.getInstanceOf("Io.Oidis.MyProject.Models.ProductRecord");

// Instantiate with initial data
const record = Reflection.getInstanceOf(
"Io.Oidis.MyProject.Models.ProductRecord",
{ Name: "Widget", Price: 9.99 }
);

This is used internally by the framework for LCP deserialization, JSONP configuration loading, and @Route resolution.

Namespace Normalization​

Reflection.NormalizeNamespace($value : string) : string

Applies namespace mappings to resolve aliases:

Reflection.NormalizeNamespace("*.AuthManager");
// → "Io.Oidis.Hub.Utils.AuthManager" (if mapped)

Instance Methods​

Access these through the singleton: Reflection.getInstance().

Class Discovery​

reflection.getAllClasses() : string[]               // All mapped class names
reflection.Exists($className : string) : boolean // Is class mapped?
reflection.getClass($className : string) : any // Get class constructor (or null)
reflection.getClassName($instance?) : string // Full name with namespace
reflection.getNamespaceName($instance?) : string // Namespace only
reflection.getClassNameWithoutNamespace($instance?) : string // Name only
const reflection = Reflection.getInstance();

// List all known classes
const allClasses = reflection.getAllClasses();
// → ["Io.Oidis.Commons.Utils.Reflection", "Io.Oidis.Commons.Models.BaseModel", ...]

// Check if a class exists
if (reflection.Exists("Io.Oidis.MyProject.Models.ProductRecord")) {
const ProductRecord = reflection.getClass("Io.Oidis.MyProject.Models.ProductRecord");
}

// Get class name for an instance
const name = reflection.getClassName(myObject);
// → "Io.Oidis.MyProject.Models.ProductRecord"

Class Hierarchy​

reflection.getClassHierarchy() : string[]
reflection.getClassHierarchyOf($instanceOrClass) : string[]
reflection.getNamespaceHierarchy() : string[]
reflection.getNamespaceHierarchyOf($instanceOrClass) : string[]
const hierarchy = reflection.getClassHierarchyOf(myRecord);
// → ["Io.Oidis.MyProject.Models.ProductRecord", "Io.Oidis.Commons.Models.BaseModel", "BaseObject"]

Type Checking​

reflection.IsInstanceOf($instance, $className) : boolean    // Exact type match
reflection.IsMemberOf($instance, $className?) : boolean // Type or subclass
reflection.IsTypeOf($className) : boolean // Current instance is type
reflection.Implements($instance, $interface?) : boolean // Implements interface
reflection.ClassHasInterface($className, $interface) : boolean // Class has interface
const reflection = Reflection.getInstance();

// Exact type check
reflection.IsInstanceOf(record, "Io.Oidis.MyProject.Models.ProductRecord"); // true

// Type or subclass check (inheritance chain)
reflection.IsMemberOf(record, "Io.Oidis.Commons.Models.BaseModel"); // true

// Interface check
reflection.Implements(record, "ISerializable"); // true if record's class implements it
reflection.ClassHasInterface("Io.Oidis.MyProject.Models.ProductRecord", "ISerializable");

Introspection​

reflection.getProperties() : string[]         // Property names (returns [])
reflection.getMethods() : string[] // Method names
reflection.getHash() : number // CRC hash of classes list
reflection.getUID() : string // Unique identifier for this instance

Serialization​

reflection.SerializationData() : object       // { classesList: [...] }
reflection.ToString($prefix?, $htmlTag?) : string // Formatted class list

@Partial Resolution​

Reflection resolves @Partial decorators during its constructor via resolvePartials(). This is a critical startup step:

  1. @Partial(MixinA, MixinB) on a class registers the mixin list in globalThis.oidisPartials
  2. When Reflection is instantiated (singleton creation), resolvePartials() runs
  3. Methods from each mixin are copied onto the target class prototype
  4. The composite class now has all methods from all mixins
// This happens automatically — you don't call resolvePartials() directly
@Partial(UsersManager, GroupsManager, SessionManager)
export class AuthManager extends BaseAuthManager {
// After Reflection initializes, AuthManager.prototype has all methods
// from UsersManager, GroupsManager, and SessionManager
}
Loader ordering

Because resolvePartials() runs in the Reflection constructor, all partial mixin classes must be registered (via ReflectionEmitter / ForceCompile.ts) before the Reflection singleton is created. This is handled automatically by the Loader chain, but be aware of it when debugging startup issues.

For full @Partial documentation, see @Partial.

When to Use Reflection Directly​

Most code interacts with Reflection indirectly through framework features:

  • @Route resolution uses getInstanceOf internally
  • @Partial uses resolvePartials internally
  • BaseObject.getUID() delegates to Reflection.UID()
  • LCP deserialization uses class discovery internally

Direct use cases:

  • Dynamic class instantiation by name string (plugin systems, config-driven creation)
  • Runtime type checking beyond instanceof (checking against namespace strings)
  • Debugging class registration issues (listing all known classes)
  • Custom serialization that needs hierarchy information

Complete Method Reference​

Static Methods​

MethodDescription
ClassName()Full class name with namespace
NamespaceName()Namespace only
ClassNameWithoutNamespace()Class name only
setInstanceNamespaces(...$values)Configure namespaces to scan
setNamespaceMappings(...$values)Configure namespace aliases
getInstance()Get singleton instance
Refresh()Re-scan namespaces and rebuild class map
getInstanceOf($className, $data?)Create instance by class name
UID($className?)Generate unique identifier
NormalizeNamespace($value)Apply namespace mappings

Instance Methods​

MethodDescription
getAllClasses()List all mapped class names
Exists($className)Check if class is mapped
getClass($className)Get class constructor
getClassName($instance?)Full name with namespace
getNamespaceName($instance?)Namespace only
getClassNameWithoutNamespace($instance?)Name only
getClassHierarchy()Hierarchy for current classes
getClassHierarchyOf($instanceOrClass)Hierarchy for given class/instance
getNamespaceHierarchy()Namespace hierarchy
getNamespaceHierarchyOf($instanceOrClass)Namespace hierarchy for given class
IsInstanceOf($instance, $className)Exact type match
IsMemberOf($instance, $className?)Type or subclass match
IsTypeOf($className)Current instance is type
Implements($instance, $interface?)Instance implements interface
ClassHasInterface($className, $interface)Class has interface
getProperties()Property names
getMethods()Method names
getHash()CRC hash of class list
getUID()Unique identifier
SerializationData()Serializable class list
ToString($prefix?, $htmlTag?)Formatted string output
ValidateProperties()Returns true
ValidateDeep($property?, $verbose?)Returns []

See also: Utilities Overview | @Partial | Architecture Overview