Typescript allows Covariance. The following code is valid, even though createElement('p')
is strongly typed and returns an HTMLParagraphElement
:
let p: HTMLElement = document.createElement('p');
At the same time, the following tsx code is not valid:
export default function RefProblem() {
const myRef = useRef<HTMLElement>(null);
return <p ref={myRef}>text</p>;
}
It produces an error message:
Type 'RefObject<HTMLElement>' is not assignable to type 'RefObject<HTMLParagraphElement>'.
This is a surprise to me, I always thought that the typechecks in Typescript would allow covariance in generic classes. So I went to the typescript playground and created this example. Everything behaves just as expected:
class A { aval: number = 0; };
class B extends A { bval: number = 0; };
let a: A = new A();
let b: B = new B();
a = b; // ok
b = a; // error
class G<T>{ gval?: T };
let ga: G<A> = new G();
let gb: G<B> = new G();
ga = gb; //ok
gb = ga; //error
Why does covariance not work for generic react refs?
This is probably due to the fact that React’s typings predate TypeScript’s variance annotations.
useRef
is typed to return RefObject<T>
. If RefObject<T>
were declared as RefObject<in T>
, the check would succeed, whereas the inverse (assigning a RefObject<HTMLParagraphElement>
to a ref
on a <span>
) wouldn’t.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With