0

Trying out polymorphism in TypeScript I found out about kind of a weird behaviour. At least I consider this a weird behaviour with myself coming from Java, where rules tend to have a much stricter tendency than in TypeScript.

So, I created a little example that showcases TypeScript only respecting the structure, but not the logical thought behind implementing and using interfaces in classes.

interface Bird {
    tweet(): string;
}

class Crow implements Bird {
    constructor(private message: string) { }

    tweet(): string {
        return this.message + "<br />";
    }
}

class Sparrow implements Bird {
    constructor(private message: string) { }

    tweet(): string {
        return this.message + "<br />";
    }
}

class ManWithAHat {
    constructor(private message: string) { }

    tweet(): string {
        return this.message + "<br />";
    }
}

class Chitter {
    static do(bird: Bird): string {
        return bird.tweet();
    }
}

// Getter for num returned 4
let element = document.getElementById('content');
let output: string = "";

const crow = new Crow("Crow says something!");
output += Chitter.do(crow);

const sparrow = new Sparrow("Sparrow says something!");
output += Chitter.do(sparrow);

const manWithAHat = new ManWithAHat("A man with a hat shouldn't be able to talk!");
output += Chitter.do(manWithAHat);

element.innerHTML = output;

The classes Crow and Sparrow both implement the interface Bird, forcing them to implement the tweet(): string method. The class ManWithAHad does not implement the Bird interface, but by coincidence has the same method tweet(): string. The static method do(bird: Bird): string in the class Chitter only accepts Bird as value. Then all three classes are instantiated and passed over to Chitter.do(bird: Bird): string.

Now, there is no error. All three classes are accepted by Chitter.do(bird: Bird): string, even the ManWithAHat class.

My question is, whether there is any case, where it acutally makes sense for Chitter.do(bird: Bird): string to also accept ManWithAHat as parameter? My second question, based on this, is whether there is a way in TypeScript to force Chitter.do(bird: Bird): string to decline any parameter not implementing the Bird interface, regardless whether the structure is the same or not.

Socrates
  • 8,724
  • 25
  • 66
  • 113
  • 1
    https://www.typescriptlang.org/docs/handbook/type-compatibility.html – Murat Karagöz May 07 '18 at 10:24
  • This seems to be the same question with different classes. If you are not happy with that answer I can reopen it, but the idea is the same – Titian Cernicova-Dragomir May 07 '18 at 10:24
  • @TitianCernicova-Dragomir Checked your answer from the other post about duck typing. I don't quite get your answer about adding privates. You say it wouldn't be compilable, but it actually is. And the strange behaviour is still the same. I updated my code example here in this post. – Socrates May 07 '18 at 10:39
  • 1
    In your case since `Bird` is an interface you can't add private to it. But the rest of the answer stands. It works because typescript uses structural compatibility. – Titian Cernicova-Dragomir May 07 '18 at 10:41
  • Ok, thanks. So there in fact is no way to force the `Bird` interface, because TypeScript only checks the structual and not the named part? – Socrates May 07 '18 at 10:49

0 Answers0