microsoft / microsoft/TypeScript

Generic parameter type checking stops too early

Open
#59,049 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

In Discussion Suggestion
Dominant language
Go
Stars
111k
Forks
14.4k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 Search Terms

generic, early, permature, defer

🕗 Version & Regression Information
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________
⏯ Playground Link

https://www.typescriptlang.org/play/?ssl=10&ssc=1&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgEoRgeQEYCsIJgA8AKgHzIDeAUMncggK5RQTgBcyJyAPsiIwA2g6gF9q1ACYFBcVshiMQhYAHsQySAGcwACgAendFjwFiAgLbZoZAJScAbquCSA3FJlyUi5WDUaAcwgwY117NAwcfEIiS2soXn4hQTJ3akFg5AdkAF5kIJCMMPdtPQdbdyA

💻 Code
interface RefObject<T> {
    current: T | null
}

declare function test(x: RefObject<number>): void;
declare function getRef(): RefObject<number | null>;

let v = getRef();
test(v);
🙁 Actual behavior

In the above code, RefObject is defined to always accept null in its current property. However, if one explicitly writes RefObject<number | null> and passes the result to a parameter accepting RefObject<number> TS reports a type error even though the types of the current property would have been the same. This happens with any type, not just null (e.g. if the definition of current was T | string.

🙂 Expected behavior

Since the current properties are equivalent after substituting the generic parameters, there should be no error. It seems like TS is stopping as soon as it sees RefObject<number | null> vs RefObject<number> even though substituting the generic parameter would result in the same type. This can be confirmed by creating a copy of the RefObject type like this:

interface RefObject<T> {
    current: T | null
}

interface RefObject2<T> {
    current: T | null
}

declare function test(x: RefObject<number>): void;
declare function getRef(): RefObject2<number | null>;

let v = getRef();
test(v);

Playground

Additional information about the issue

This affects the change in React's types between version 18 and 19, where the RefObject type changed current from T | null to only T. The uses of RefObject should add null instead, e.g. RefObject<HTMLDivElement | null>. Making libraries compatible with both the React 18 and React 19 types is difficult due to this TS issue. If a library updates its implementation to React 19, including RefObject<HTMLElement | null> in its definitions, consumers still using React 18 types will see TS errors.

cc. @eps1lon in case you know about this

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the linked TypeScript Playground reproductions and compare the RefObject and RefObject2 cases. Trace the type-checking path for generic instantiation and structural assignability; the issue names no repository files or tests. Done means the equivalent substituted property types are accepted without breaking existing generic checking behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.