A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration. — Source.
For example, given the decorator @sealed we might write the sealed function as follows:
function sealed(target) { // do something with 'target' ...}
Here @sealed is a class decorator applied just above the class declaration. This @sealed is a decorator that is applied at run time.
If you want to prevent any modifications to the class BugReport, you could define sealed function as below:
function sealed(constructor: Function) { Object.seal(constructor); Object.seal(constructor.prototype);}
When @sealed is executed, it will seal both the constructor and its prototype, and will therefore prevent any further functionality from being added to or removed from this class during runtime by accessing BugReport.prototype or by defining properties on BugReport itself — Source
This is a decorator factory that returns an arrow function that is executed at run time. You can read more about decorator factory in TS docs.
export function Component(options: ComponentOptions) { // _context is ClassDecoratorContext, but that then requires a public constructor // which Application does not have. return (target: Function, _context: unknown) => { const proto = target.prototype; if (!(proto instanceof AbstractComponent)) { throw new Error( "The `Component` decorator can only be used with a subclass of `AbstractComponent`.", ); } if (options.childClass) { if (!(proto instanceof ChildableComponent)) { throw new Error( "The `Component` decorator accepts the parameter `childClass` only when used with a subclass of `ChildableComponent`.", ); } childMappings.push({ host: proto, child: options.childClass, }); } const name = options.name; if (name) { proto.componentName = name; } // If not marked internal, and if we are a subclass of another component T's declared // childClass, then register ourselves as a _defaultComponents of T. const internal = !!options.internal; if (name && !internal) { for (const childMapping of childMappings) { if (!(proto instanceof childMapping.child)) { continue; } const host = childMapping.host; host["_defaultComponents"] = host["_defaultComponents"] || {}; host["_defaultComponents"][name] = target as any; break; } } };}
There is a lot happening in this Component decorator, instead of trying to understand it all, let’s pick up on the easy ones we can deduce.
proto instanceOf
This check is used to throw an error in case the instance is not supported.
2. proto.componentName
proto.componentName is updated based on name passed to the decorator. In this case, the name is set to “application”.
3. childMappings
// If not marked internal, and if we are a subclass of // another component T's declared// childClass, then register ourselves as a _defaultComponents of T.const internal = !!options.internal;if (name && !internal) { for (const childMapping of childMappings) { if (!(proto instanceof childMapping.child)) { continue; } const host = childMapping.host; host["_defaultComponents"] = host["_defaultComponents"] || {}; host["_defaultComponents"][name] = target as any; break; }}
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.