Something tricky in TypeScript
Interfaces with excess properties
TypeScript lets us pass { size: number; label: string; } to something that only expected a { label: string; }, since TS has a structural subtyping system. But inline literal objects does not.
It is caused by excess properties check,
Play with the sample
Classes (nominal typing)
Also caused by structural typing. If class A and class B share the same members, the functions which require an instance of A, also accepts B as legal parameter.
Try the sample
Discriminated Unions
TypeScript has a feature called discriminated unions. Quoting from the docs, there are 2 requirements for discriminated unions:
- Types that have a common, singleton type property — the discriminant.
- A type alias that takes the union of those types — the union.
It means in case:
interface Dog {
kind: "dog"
bark: string
}
interface Cat {
kind: "cat"
meow: string
}
type Animal = Cat | Dog
The type Animal
is expected have property kind
on it.
However, it does not work if the common property is a nested object, like:
interface Dog {
taxonomy: {
species: "Canis familiaris"
}
bark: string
}
interface Cat {
taxonomy: {
species: "Felis catus"
}
meow: string
}
Play with the sample
References
TypeScript’s quirks: How inconsistencies make the language more complex