I’m so confused, when I assign a superset type to their subset type in the useState
hook, why would Typescript not complain about mismatched type? And if it’s intended, how should I type the useState
hook to make it complain properly?
`
interface Animal {
name: string;
food: string;
legs: number;
}
interface Dog {
name: string;
food: string;
}
const animal: Animal = {
name: 'animal',
food: 'animal food',
legs: 4,
};
function App() {
const [data, setData] = useState<Dog>(animal);
...
}
2
Answers
A static type checker can use either the name (nominal typing) or the structure (structural typing) of types when comparing them against other types (like when checking if one is a
subtype
of another).TypeScript uses a structure typing system.
See Type Compatibility.
V.S. Nominal typing
Languages like C++, Java, and Swift have primarily nominal type systems.
Languages like Go, TypeScript and Elm have primarily structural type systems
One way to declare nominal typing in TypeScript:
Playground Link
Nominal typing proposal: https://github.com/Microsoft/Typescript/issues/202
The
Brand<T, U>
type ofutility-types
package.the assignment to be valid due to type compatibility rules.
TypeScript allows assigning a superset type to a subset type because the superset type contains all the properties and values required by the subset type.
If you want TypeScript to properly complain about the mismatched type, you can explicitly type the
useState()
hook with the desired type. In this case, you would want to specify Animal as the type for theuseState()
hook, as it matches the animal object you are initializing it with.Remember that TypeScript type checks are performed statically at compile time,