Accepted answer

Yeah, this is a quirk of how the typings are written:

function useRef<T>(initialValue: T): MutableRefObject<T>;
function useRef<T>(initialValue: T|null): RefObject<T>;

If the initial value includes null, but the specified type param doesn't, it'll be treated as an immutable RefObject.

When you do useRef<HTMLInputElement>(null), you're hitting that case, since T is specified as HTMLInputElement, and null is inferred as HTMLInputElement | null.

You can fix this by doing:

useRef<HTMLInputElement | null>(null)

Then T is HTMLInputElement | null, which matches the type of the first argument, so you hit the first override and get a mutable ref instead.


I came to this question by searching how to type useRef with Typescript when used with setTimeout or setInterval. The accepted answer helped me solve that.

You can declare your timeout/interval like this

const myTimeout = useRef<ReturnType<typeof setTimeout> | null>(null)

And to clear it and set it again, you do it as usual:

const handleChange = () => {
  if (myTimeout.current) {
  myTimeout.current = setTimeout(() => {
  }, 500)

The typing will work both if you're running in Node or in a Browser.

Related Query

More Query from same tag