The problem is that you're trying to narrow the type of animalDto.information based on animalDto.type, which will only work if animalDto is a union of DTOs for specific animals (more on that below).
The usual way to do this is to not have an Animal base class, and instead have a union type of specific animal types (this is called a discriminated union — a union where you can tell the members apart by some characteristic [name in this case]):
interface Bird {
name: "bird";
canFly: boolean;
}
interface Dog {
name: "dog";
tailLength: number;
}
type Animal = Bird | Dog;
(I've used interfaces there, but you could do it with Bird and Dog classes as well.)
That way, we can differentiate animals based on the type of their name (which is a string literal type, "bird" or "dog", not just a string):
interface AnimalDTO {
information: Animal;
}
function registerAnimal(animalDto: AnimalDTO) {
const animal = animalDto.information;
if (animal.name == "dog") {
petDog(animal);
}
}
function petDog(dog: Dog) {
console.log(`Petting ${dog.name} with tail length ${dog.tailLength}`);
}
Playground link
If AnimalDTO has to have a separate type, you can do the same thing with it that I did with Bird and Dog above:
interface BirdDTO {
type: "bird";
information: Extract<Animal, {name: "bird"}>;
}
interface DogDTO {
type: "dog";
information: Extract<Animal, {name: "dog"}>;
}
type AnimalDTO = BirdDTO | DogDTO;
function registerAnimal(animalDto: AnimalDTO) {
if (animalDto.type == "dog") {
petDog(animalDto.information);
}
}
The Extract bit identifies the specific name with a name matching the type we're using for the DTO.
Playground link
If there are a lot of animals, repeating "bird" and "dog" in both places is error-prone, so we can use a generic type to create them:
type MakeAnimalDTO<AnimalType extends string> = {
type: AnimalType;
information: Extract<Animal, {name: AnimalType}>;
}
type BirdDTO = MakeAnimalDTO<"bird">;
type DogDTO = MakeAnimalDTO<"dog">;
type AnimalDTO = BirdDTO | DogDTO;
Playground link
There are lots of ways to do this, but the fundamental thing is to have a type-based way of differentiating the types from one another; in the above that's the "bird" and "dog" string literal types.