Search code examples
interfacerequirejstypescriptamd

TypeScript: Reference interface in class when using AMD


I set up everything so far to use TypeScript with requirejs. I tested my scenario with to classes and everything works fine but I fail when I try to use interface definitions.

IComponent.ts:

/// <reference path="Actor.ts" />

interface IComponent {
  owner: Actor

  init()
  update(delta: number)
  render()
}

Actor.ts:

/// <reference path="IComponent.ts" />

class Actor {
    private components: IComponent[] = []

    add(component: IComponent) {
        component.owner = this;
        this.components.push(component);
    }

    remove(component: IComponent) {
        var index = this.components.indexOf(component);
        this.components.splice(index, 1);
    }

    update(delta: number) {
        this.components.forEach(c => c.update(delta));
    }

    render() {
        this.components.forEach(c => c.render());
    }

    init() {
        this.components.forEach(c => c.init());
    }
}

export = Actor

I get an error in my IComponent.ts: The symbol 'Actor' could not be found. How can I reference my Actor class from my interface?


Solution

  • As it currently stands you have a logical circular reference, Actor depends on IComponent and IComponent depends on Actor.

    Your IComponent interface is little use without your Actor class, so my recommended solution would be to place the IComponent interface within your actor.ts file. This way, the dependency is self-sufficient.

    export interface IComponent {
        owner: Actor;
    
        init();
        update(delta: number);
        render();
    }
    
    export class Actor {
        private components: IComponent[] = []
    
        add(component: IComponent) {
            component.owner = this;
            this.components.push(component);
        }
    
        remove(component: IComponent) {
            var index = this.components.indexOf(component);
            this.components.splice(index, 1);
        }
    
        update(delta: number) {
            this.components.forEach(c => c.update(delta));
        }
    
        render() {
            this.components.forEach(c => c.render());
        }
    
        init() {
            this.components.forEach(c => c.init());
        }
    }
    

    Alternatively, you could make IComponent depend on an abstraction of Actor rather than on the concrete class (in your case, perhaps an IActor. Now your Actor depends on IComponent, but that is the end of the chain.

    export interface IComponent {
        owner: IActor;
    
        init();
        update(delta: number);
        render();
    }
    
    export interface IActor {
        add(component: IComponent);
        remove(component: IComponent);
        update(delta: number);
        render();
        init();
    }
    

    Hacky Fix:

    This resolves the error in the IComponent.ts file, but not in a nice way.

    import Actor = require('actor');
    
    interface IComponent {
        owner: Actor;
    
        init();
        update(delta: number);
        render();
    }