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