> Why was the `dogs` array initialized as an `Animal[]` type instead of `Dog[]`
That might be your confusion: it wasn't. Its type is `Dog[]`.
> which would forbid the addition of a `Cat` type?
Why would that be forbidden? The problematic method is `append_animals`, which only cares that both arguments satisfy `Animal`, which both `Dog` and `Cat` do.
> Why would you be able to map a call to `woof` over an `Animal[]` when `Animal` doesn't implement `woof`
Back to your root confusion, since for all intents and purposes, `dogs.map` thinks it's an array of dogs, it doesn't complain.
If `append_animals` was written like this, things would be fine:
let append_animals = <T extends Animal>(animals: T[], animal: T) => animals.push(animal)