Transitioning to TypeScript: The Ultimate Starter Guide - Part 7

TypeScript from JavaScript Part 7 of 7: Decorators and Mixins

Hello again, amazing developers! We have finally reached the last part of our journey from JavaScript to TypeScript. In this concluding part of the series, we will explore the advanced features of decorators and mixins in TypeScript.

As a reminder, this series is tailored for beginner developers who are looking for a starting point and exciting ways to infuse new technology into their programming journey. So, let’s dive right into decorators and mixins!

1. Decorators

Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript. They provide a way to add annotations and a meta-programming syntax for class declarations and members. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

a. Class Decorators

A Class Decorator is declared just before a class declaration. The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.

function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

In this example, @sealed is a class decorator that seals both the constructor and its prototype.

b. Method Decorators

A Method Decorator is declared just before a method declaration. The method decorator is applied to the Property Descriptor for the method and can be used to observe, modify, or replace a method definition.

function enumerable(value: boolean) {
    return function (
        target: any,
        propertyKey: string,
        descriptor: PropertyDescriptor
    ) {
        descriptor.enumerable = value;
    };
}

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }

    @enumerable(false)
    greet() {
        return "Hello, " + this.greeting;
    }
}

In this example, @enumerable(false) is a method decorator that sets the enumerable property of the greet method to false.

c. Property Decorators

A Property Decorator is declared just before a property declaration. The property decorator cannot be used to observe or modify a property descriptor. It is only passed the name of the property.

function format(formatString: string) {
    return function (
        target: any,
        propertyKey: string
    ) {
        let _value: any;
        const getter = function () {
            return _value;
        };
        const setter = function (newVal: any) {
            _value = newVal;
        };
        const object = {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        };
        Object.defineProperty(target, propertyKey, object);
    };
}

class Greeter {
    @format("Hello, %s")
    greeting: string;

    constructor(message: string) {
        this.greeting = message;
    }
}

In this example, @format("Hello, %s") is a property decorator that applies a format to the greeting property.

2. Mixins

Mixins are a way of building up classes from reusable components by copying the methods from one class into another, without using inheritance.

a. Creating Mixins

To create a mixin, you can create a class that implements a new interface that extends the interface of the classes you are mixing in.

class Disposable {
    isDisposed: boolean;
    dispose() {
        this.isDisposed = true;
    }
}

class Activatable {
    isActive: boolean;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false;
    }
}

interface DisposableActivatable extends Disposable, Activatable {}
class SmartObject implements DisposableActivatable {
    constructor() {
        setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
    }

    interact() {
        this.activate();
    }

    // Disposable
    isDisposed: boolean = false;
    dispose: () => void;
    // Activatable
    isActive: boolean = false;
    activate: () => void;
    deactivate: () => void;
}

applyMixins(SmartObject, [Disposable, Activatable]);

const smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000);

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

In this example, DisposableActivatable is an interface that extends both Disposable and Activatable. SmartObject is a class that implements DisposableActivatable, and applyMixins is a helper function that copies the methods from `

DisposableandActivatableintoSmartObject`.

Conclusion

In this final part of the TypeScript from JavaScript series, we delved deep into the world of decorators and mixins, two advanced features of TypeScript that can help you create more robust and reusable code.

Decorators provide a way to add annotations and a meta-programming syntax for class declarations and members, while mixins are a way of building up classes from reusable components without using inheritance.

Congratulations on completing this series! You are now well-equipped to start your journey as a TypeScript developer. Remember, the key to mastery is practice, so don't stop coding!

Happy coding, everyone! 💻✨


If you are just joining in, here are the links to the other parts of this series:

  1. TypeScript from JavaScript Part 1: Introduction and Basic Types

  2. TypeScript from JavaScript Part 2: Interfaces and Classes

  3. TypeScript from JavaScript Part 3: Functions and Generics

  4. TypeScript from JavaScript Part 4: Enums and Type Inference

  5. TypeScript from JavaScript Part 5: Advanced Types

  6. TypeScript from JavaScript Part 6: Modules and Namespaces


That’s a wrap. Thanks for reading.

If you're looking for more premium content and resources to help you start and grow your business, consider subscribing to my Newsletter.

Want to see what I am working on? Check out my Twitter

Did you find this article valuable?

Support Innovate Sphere by becoming a sponsor. Any amount is appreciated!