microsoft / microsoft/TypeScript

Needless evaluation of placeholder type when checking that an interface matches a constraint

Open
#52,356 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Bug Report

🔎 Search Terms

type constraint, interface, evaluation, type checker, placeholder type

🕗 Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about this

⏯ Playground Link

Playground link with relevant code

💻 Code

Note: the utility type Zeros is designed to infinitely recur when it is fed unknown in order to exhibit the problem.

type Zeros<N, R extends unknown[] = []> =
    N extends R['length'] ? R : Zeros<N, [0, ...R]>;

interface $Zeros { 0: unknown; type: Zeros<this[0]>; }

type apply$Zeros = apply<$Zeros, [3]>;
//                 ~~~~~~~~~~~~~~~~~~
// Type instantiation is excessively deep and possibly infinite

type apply<$T extends { type: unknown }, Args> = ($T & Args)['type'];

Alternate implementations of apply which don't reproduce the error can be found bellow:

// no type constraint
type apply<$T, Args> = ($T & Args)['type' & keyof $T];

// type constraint cheking keyof
type apply<$T extends (keyof $T extends keyof Type ? unknown : never), Args> =
    ($T & Args)['type' & keyof $T];
🙁 Actual behavior

It is my understanding that the type constraint $T extends { type: unknown } causes the type checker to evaluate Zeros<this[0]> as Zeros<unknown>, which leads to infinite recursion.

I believe this evaluation was not necessary because every type extends unknown, including undefined or never (an optional type field could also be caught without having to evaluate the value), therefore I consider that $T extends { type: unknown } could only check that the type key exists in $T without relaxing type safety.

I imagine that skipping this evaluation would also improve performance.

The alternative type safe solution of manually checking the existence of the keys is not user friendly.

🙂 Expected behavior

$T extends { type: unknown } should only check that the type field exists and enable to index $T & Args with type with no unexpected side-effect.

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 reproducing the linked Playground example and compare it with the two alternate apply implementations in the issue. The payload names no TypeScript source file or test, so tracing the type-checker constraint evaluation will require repository familiarity. Done means the constraint accepts the intended type without evaluating the recursive placeholder and existing type-safety behavior remains intact.

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
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.