Why this does not work in typescript?
class Parent {
id: string = ''
}
class Child extends Parent{
name: string = ''
}
const fails: (created: Parent) => void = (created: Child) => { return };
const failsToo: ({ created }: { created: Parent }) => void = ({ created }: { created: Child }) => { return };
At least for me the error is very weird:
Type '(created: Child) => void' is not assignable to type '(created: Parent) => void'.
Types of parameters 'created' and 'created' are incompatible.
Property 'name' is missing in type 'Parent' but required in type 'Child'
It seems like it is trying to assign a Parent to a Child, but the in the real code is backwards (Trying to assign a method parameter that is a Child to a Parent. Which it make sense because Child is a super set of Parent)
Am I missing something?
CodePudding user response:
Function types are contravariant in their parameter types. The types counter-vary. They vary in the opposite direction from each other. So if A extends B
, then ((x: B)=>void) extends ((x: A)=>void)
, not ((x: A)=>void) extends ((x: B)=>void)
. This is a natural consequence of type theory, but you could convince yourself of this necessity by imagining trying to pass narrower/wider types to functions than they expect and seeing what happens. For example, imagine this succeeded:
const fails: (created: Parent) => void =
(created: Child) => { created.name.toUpperCase() };
The function (created: Child) => { created.name.toUpperCase() }
is fine by itself; it accepts a Child
and accesses its name
property, which is a string
, so it has a toUpperCase()
method. But you've assigned it to a variable of type (created: Parent) => void
. And that means you can call fails()
like this:
fails(new Parent()); // okay at compile time, but
//