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 — Change in generic behavior between 3.0.1 and >=3.1.1 · Issue #27895 · microsoft/TypeScript

Change in generic behavior between 3.0.1 and >=3.1.1 #27895

New issue
Copy link
New issue
Copy link
Closed
#30769
Closed
Change in generic behavior between 3.0.1 and >=3.1.1#27895
#30769
Copy link
Assignees
ahejlsberg
Labels
BugA bug in TypeScriptA bug in TypeScriptDomain: Indexed Access TypesThe issue relates to accessing subtypes via index accessThe issue relates to accessing subtypes via index accessFixedA PR has been merged for this issueA PR has been merged for this issue
Milestone
TypeScript 3.4.0

Description

@chrisbouchard
Chris Bouchard (chrisbouchard)
opened on Oct 14, 2018
Issue body actions

TypeScript Version: 3.2.0-dev.20181011
This code worked in 3.0.1, and is broken >=3.1.1.

Search Terms:
"is not assignable to type"

Code

export interface Entity {
    id: number | string;
}

export type IdOf<E extends Entity> = E['id'];

export interface EntityState<E extends Entity> {
    ids: IdOf<E>[];
    entities: { [key: string]: E, [key: number]: E };
}


export function getAllEntities<E extends Entity>(state: EntityState<E>): E[] {
    const { ids, entities } = state;
    return ids.map(id => entities[id]);
}

export function getEntity<E extends Entity>(id: IdOf<E>, state: EntityState<E>): E | undefined {
    const { ids, entities } = state;

    if (!ids.includes(id)) {
        return undefined;
    }

    return entities[id];
}

For completeness, I'll just mention that I intend for users to create subinterfaces of Entity and EntityState for their domain types (like @ngrx/entity), overriding id to be a more restrictive type. This is why the IdOf type is important. E.g.,

interface Task extends Entity {
    id: string;
    due: Date;
    title: string;
}

interface TaskState extends EntityState<Task> {
    // Inherits EntityState#ids as string[]
    currentTask?: string;
}

Expected behavior:
The code compiles without error.

Actual behavior:

BUILD ERROR
projects/entity/src/lib/entity-state.utils.ts(13,5): error TS2322: Type '{ [key: string]: E; [key: number]: E; }[E["id"]][]' is not assignable to type 'E[]'.
  Type '{ [key: string]: E; [key: number]: E; }[E["id"]]' is not assignable to type 'E'.
    Type 'Entity' is not assignable to type 'E'.
projects/entity/src/lib/entity-state.utils.ts(23,5): error TS2322: Type '{ [key: string]: E; [key: number]: E; }[E["id"]]' is not assignable to type 'E | undefined'.
  Type 'Entity' is not assignable to type 'E'.
    Type '{ [key: string]: E; [key: number]: E; }[E["id"]]' is not assignable to type 'E'.
      Type 'Entity' is not assignable to type 'E'.

Playground Link:
Playground Link

Related Issues:
I did not find any that seemed similar, but I'm also unsure what the actual underlying problem is. I did ask on StackOverflow, and they suggested I ask here.

Activity

  1. mattmccutchen commented on Oct 14, 2018

    @mattmccutchen
    Matt McCutchen (mattmccutchen)
    on Oct 14, 2018
    Contributor
    More actions

    This was caused by #26698. The old behavior to check assignability of the indexed access type { [key: string]: E; [key: number]: E; }[E["id"]] to E was to take the base constraint of both the object type and the index type, which gave { [key: string]: E; [key: number]: E; }[string | number] = E, which worked. The new behavior takes the constraint of the object type, and if there isn't one, jumps straight to the base constraint of the whole indexed access type, so it goes to Entity and misses E.

  2. chrisbouchard commented on Oct 14, 2018

    @chrisbouchard
    Chris Bouchard (chrisbouchard)
    on Oct 14, 2018
    Author
    More actions

    Is this considered a regression? Or, if this is the intended new behavior, is there a suggested workaround?

  3. mattmccutchen commented on Oct 14, 2018

    @mattmccutchen
    Matt McCutchen (mattmccutchen)
    on Oct 14, 2018
    Contributor
    More actions

    I'd call it a regression, but it's not my opinion that matters.

    A workaround is to upcast id to string | number:

    export function getAllEntities<E extends Entity>(state: EntityState<E>): E[] {
        const { ids, entities } = state;
        const ids2: (string | number)[] = ids;
        return ids2.map(id => entities[id]);
    }
    
    export function getEntity<E extends Entity>(id: IdOf<E>, state: EntityState<E>): E | undefined {
        const { ids, entities } = state;
    
        if (!ids.includes(id)) {
            return undefined;
        }
    
        const id2: string | number = id;
        return entities[id2];
    }
    Reacted by Chris Bouchard, Titian Cernicova-Dragomir and NN
  4. Wesley Wigham (weswigham)
    added
    BugA bug in TypeScriptA bug in TypeScript
    Domain: Indexed Access TypesThe issue relates to accessing subtypes via index accessThe issue relates to accessing subtypes via index access
    on Oct 16, 2018
  5. Daniel Rosenwasser (DanielRosenwasser)
    assigned
    Anders Hejlsberg (ahejlsberg)
    on Oct 25, 2018
  6. Daniel Rosenwasser (DanielRosenwasser)
    added this to the TypeScript 3.2 milestone on Oct 25, 2018
  7. Daniel Rosenwasser (DanielRosenwasser)
    modified the milestones: TypeScript 3.2, TypeScript 3.2.1 on Nov 13, 2018
  8. Daniel Rosenwasser (DanielRosenwasser)
    modified the milestones: TypeScript 3.2.1, TypeScript 3.3.0 on Nov 29, 2018
  9. Anders Hejlsberg (ahejlsberg)
    modified the milestones: TypeScript 3.3, TypeScript 3.4.0 on Jan 16, 2019
  10. Anders Hejlsberg (ahejlsberg)
    closed this as completedin #30769on Apr 12, 2019
  11. Anders Hejlsberg (ahejlsberg)
    added
    FixedA PR has been merged for this issueA PR has been merged for this issue
    on Apr 12, 2019
  12. 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

  • @ahejlsberg
    Anders Hejlsberg (ahejlsberg)

Labels

BugA bug in TypeScriptA bug in TypeScriptDomain: Indexed Access TypesThe issue relates to accessing subtypes via index accessThe issue relates to accessing subtypes via index accessFixedA PR has been merged for this issueA PR has been merged for this issue

Type

No type

Projects

No projects

    Milestone

    • TypeScript 3.4.0

    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.