GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — keyof ignores constraint · Issue #44985 · microsoft/TypeScript

keyof ignores constraint #44985

New issue
Copy link
New issue
Copy link
Closed
Closed
keyof ignores constraint#44985
Copy link
Labels
QuestionAn issue which isn't directly actionable in codeAn issue which isn't directly actionable in code

Description

@alesmenzel
Aleš Menzel (alesmenzel)
opened on Jul 12, 2021
Issue body actions

Bug Report

🔎 Search Terms

keyof, generic constraint

🕗 Version & Regression Information

  • This is a crash ❌ No
  • This changed between versions _______ and _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about keyof
  • I was unable to test this on prior versions because it seems to be consistent behavior in all tested versions

⏯ Playground Link

Playground link with relevant code

💻 Code

// Implementation of event emitter - sample without all methods
type Listener<Event> = (event: Event) => void;

// Here the "key: K" complains "An index signature parameter type must be either 'string' or 'number'"
type Listeners<Events, K extends string & keyof Events> = {[key: K]: Listener<Events[K]>[]}

class EventEmitter<Events extends {[key: string]: unknown}> {
  #listeners: Listeners<Events, keyof Events>

  constructor() {
    this.#listeners = {}
  }
}

// Usage
type MyEvents = {
    connect: void
    reconnect: number
    error: Error
    disconnect: void
}
const emitter = new EventEmitter<MyEvents>()

🙁 Actual behavior

I am getting ts errors even though the types work as expected. See // usage section

🙂 Expected behavior

The keyof returns the same type as the constraint generic and thus will be valid as index signature.

Activity

  1. MartinJohns commented on Jul 12, 2021

    @MartinJohns
    Martin Johns (MartinJohns)
    on Jul 12, 2021
    Contributor
    More actions

    Having keyof T as an indexer type would require #26797.

  2. alesmenzel commented on Jul 12, 2021

    @alesmenzel
    Aleš Menzel (alesmenzel)
    on Jul 12, 2021
    Author
    More actions

    So, does that mean it is currently not possible to do?
    btw. why is K extends string & keyof Events not enough to make it an indexable type since it requires 'string' or 'number' and the generic K must be a string and also keyof Events?

  3. MartinJohns commented on Jul 12, 2021

    @MartinJohns
    Martin Johns (MartinJohns)
    on Jul 12, 2021
    Contributor
    More actions

    The type string & keyof Events is the same as keyof Events. And a union of keys can not be an index signature. You can use a mapped type instead.

    Reacted by Andrii Dieiev and 流浪大法师
  4. Ryan Cavanaugh (RyanCavanaugh)
    added
    QuestionAn issue which isn't directly actionable in codeAn issue which isn't directly actionable in code
    on Jul 12, 2021
  5. RyanCavanaugh commented on Jul 12, 2021

    @RyanCavanaugh
    Ryan Cavanaugh (RyanCavanaugh)
    on Jul 12, 2021
    Member
    More actions

    This works without issue

    type Listeners<Events, K extends keyof Events> = Record<K, Listener<Events[K]>>;
  6. alesmenzel commented on Jul 12, 2021

    @alesmenzel
    Aleš Menzel (alesmenzel)
    on Jul 12, 2021
    Author
    More actions

    Thanks guys,
    the error message wasnt clear to me, but know I understand that you cant use a union of types which you get from the keyof - e.g. type union = 'a' | 'b' | 'c' to index but instead use it in either Record which handles the union case or use mapped type like {[key in K]: ... } instead of {[key: K]:

    Lastly, do you know why there is the last error at the .push(listener) line in .on() method ts playground

    Argument of type 'Listener<Events[Type]>' is not assignable to parameter of type 'Listener<Events[keyof Events]>'.
      Type 'Events[keyof Events]' is not assignable to type 'Events[Type]'.
        Type 'keyof Events' is not assignable to type 'Type'.
          'keyof Events' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint 'string | number | symbol'.
            Type 'string | number | symbol' is not assignable to type 'Type'.
              'string | number | symbol' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint 'string | number | symbol'.
    
                Type 'string' is not assignable to type 'Type'.
                  'string' is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint 'string | number | symbol'.(2345)
    

    it seems to me that those types should be the same, no? One is Events[keyof Events] (so basically any values of the Events) and the second one is the concrete event Events[Type] where the type is defined as constraint of keyof Events

      on<Type extends keyof Events>(type: Type, listener: Listener<Events[Type]>): Unsubscribe  {
        if (!type) throw new Error('Cannot call <EventEmitter>.on without a type');
        if (!listener) throw new Error('Cannot call <EventEmitter>.on without a listener');
        this.#listeners[type] = this.#listeners[type] || [];
        this.#listeners[type].push(listener);
        return () => {
          this.off(type, listener);
        }
      }
  7. alesmenzel commented on Jul 12, 2021

    @alesmenzel
    Aleš Menzel (alesmenzel)
    on Jul 12, 2021
    Author
    More actions

    Looks like I had a mistake in the mapped type, working version for anyone interested
    ts playground

    Thanks Martin Johns (@MartinJohns) and Ryan Cavanaugh (@RyanCavanaugh)

  8. Aleš Menzel (alesmenzel)
    closed this as completedon Jul 12, 2021
  9. microsoft
    locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in codeAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.