Mick
Mick

Reputation: 8913

TypeScript factory pattern says property doesnt exist

Im trying to implement the factory pattern in TypeScript, but i can't access the child-functions that doesnt exist in the super class. (It works, but the compiler is giving me an error).

Structure:

abstract class Animal {
    walk(meters:number) { ... }
}

class Elephant extends Animal {
    walk(meters:number) { ... }
}

class Eagle extends Animal {
    walk(meters:number) { ... }
    fly(meters:number) { ... }
}

My factory:

class Zoo {
    animals:Animal[] = [];

    addAnimal(type:string): Animal {
        var a: Animal;

        switch(type) {
            case 'elephant':
                a = new Elephant();
                break;
            case 'eagle':
                a = new Eagle();
                break;
            default:
                throw new Error('Animal of type \'' + type + '\' doesn\t exist');
        }

        this.animals.push(a);
        return a;
    }
}

Then:

var sammy:Animal = addAnimal('eagle');
sammy.fly(15);

This gives me: Error: TS2339: Property 'fly' does not exist on type 'Animal'.

Also i tried to cast:

var sammy:Eagle = addAnimal('eagle');
sammy.fly(15)

Which gives me: Error: TS2322: Type 'Animal' is not assignable to type 'Eagle'. Property 'fly' is missing in type 'Animal'.

I made a code playground on TypeScript page: http://bit.ly/21yXXjf

Upvotes: 0

Views: 458

Answers (2)

Alex Cheremisin
Alex Cheremisin

Reputation: 248

You are returning an animal with type Animal and then trying to access property 'fly', which does not exists in this type, possible solution is to add property 'fly' to Animal class, or remove Animal type from addAnimal method addAnimal(type:string): Animal {}, or you could change your code to this:

var sammy = <Eagle>myZoo.addAnimal('eagle'); // This woudl work without errors!

Just add <Eagle> type to your method call

Upvotes: 0

Fenton
Fenton

Reputation: 250882

Quick Fix

You can use type assertions to take the type checking away from TypeScript and into your own hands.

var sammy = <Eagle><any>zoo.addAnimal('eagle');
sammy.fly(15)

This can result in problems, so there is a better solution to your problem (and the factory problem in general)...

Better Solution

Use specialized signatures to return the correct type based on the static string:

class Zoo {
    animals:Animal[] = [];

    addAnimal(type: 'elephant'): Elephant;
    addAnimal(type: 'eagle'): Eagle;
    addAnimal(type: string): Animal;
    addAnimal(type: string): Animal {
        var a: Animal;

        switch(type) {
            case 'elephant':
                a = new Elephant();
                break;
            case 'eagle':
                a = new Eagle();
                break;
            default:
                throw new Error('Animal of type \'' + type + '\' doesn\t exist');
        }

        this.animals.push(a);
        return a;
    }
}

var zoo = new Zoo();

// No type assertion needed, sammy is an eagle!
var sammy = zoo.addAnimal('eagle');
sammy.fly(15)

Upvotes: 2

Related Questions