1

I have the following code, which works in TypeScript 3.0.1 but gives compile errors in 3.1.3. If possible, I'd like to know what changed in 3.1.+ that makes the code invalid, or if this may be a compiler bug.

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];
}

As I said, it compiles fine in 3.0.1, but in 3.1.3 I get the following errors:

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'.

The two errors correspond to the two functions' return statements, respectively.

Edit. For completeness, I'll just mention that I intend for users to create subinterfaces of Entity and EntityState for their domain types, 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;
}
2
  • 1
    Quite frankly this seems like either an undocumented breaking change, or a regression. I would suggest you submit it on GitHub and see what the team says. Commented Oct 14, 2018 at 8:07
  • @TitianCernicova-Dragomir I created github.com/Microsoft/TypeScript/issues/27895, we'll see what happens. Thanks. Commented Oct 14, 2018 at 13:31

1 Answer 1

1

Changing type to interface fixes the problem, type doesn't support extending, always use interface if you need inheritance to work, the fact that it was working earlier is confusing, maybe they added more strict rules to generic types checking

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]) as E[];
}

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] as E;
}
Sign up to request clarification or add additional context in comments.

Your Answer

Draft saved
Draft discarded

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.