It's not an explicit feature, but a combination of arbitrary records, sum types, and type narrowing:
type Animal =
| { species: "dog", name: string, catsChased: number }
| { species: "cat", name: string }
const animal: Animal = ...
// Available in all cases
console.log(animal.name)
if (animal.species === "dog") {
// Can access dog attrs here
console.log(animal.catsChased)
}
// ERROR: catsChased only exists for dog and not for cat
animal.catsChased