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:
- Scanning registered namespaces on
globalThis(e.g.,globalThis.Io.Oidis.*) - Building an internal map of all discovered classes
- 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:
@Partial(MixinA, MixinB)on a class registers the mixin list inglobalThis.oidisPartials- When
Reflectionis instantiated (singleton creation),resolvePartials()runs - Methods from each mixin are copied onto the target class prototype
- 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
}
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:
@Routeresolution usesgetInstanceOfinternally@PartialusesresolvePartialsinternallyBaseObject.getUID()delegates toReflection.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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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