Index signature for type 'string' is missing in type. / KnownKeys

Viewed 1588

I am trying to enable strict in the application and I had interfaces that looked roughly like this:

interface Item {
  id: string;
  archivedAt: Date | string;
  secondItem: SecondItem
}

interface SecondItem {
  name: string;
  content: string;
}

My problem was, that when creating a new object of type Item with extra values, the payload should be created. E.g.

data['someOtherKey'] = 'someOtherValue';

const item = new ItemCreatedPayload(data); // this should not throw, further description below

Instead I recieved the following error: Element implicitly has an 'any' type because expression of type '"someOtherKey"' can't be used to index type Item

I managed to solve this by refactoring the interface:

interface Item {
  id: string;
  archivedAt: Date | string;
  secondItem: SecondItem;
  [key: string]: unknown;
}

Here is my first question: is there another way to achieve this?

This works fine in tests, but errors in the app proper. That's how it's being used:

export class ItemCreatedPayload {
  constructor(
    public readonly item: Item,
  ) {
    const schema = Joi.object({ item: ItemCreatedSchema.required() });
    const { error } = schema.validate({ item }, { allowUnknown: true });

    if (error) {
      throw error;
    }
  }
}

Then I'm emitting the event via nats microservice

this.events.emitItemCreatedEvent(new ItemCreatedPayload(item));

And I get this error: Index signature for type 'string' is missing in type 'item'.

I managed to get around this as well by using KnownKeys type:

type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K;
} extends { [_ in keyof T]: infer U }
  ? U
  : never;

Like this:

export class ItemCreatedPayload {
  constructor(
    public readonly item: Pick<Item, KnownKeys<Item>>,
  ) {
    const schema = Joi.object({ item: ItemCreatedSchema.required() });
    const { error } = schema.validate({ item }, { allowUnknown: true });

    if (error) {
      throw error;
    }
  }
}

This solves the Index signature for type 'string' is missing in type 'Item'. issue, but introduces a new one. Now in the event handler I am unable to access any item properties

  @EventPattern(EventName.ItemCreated)
  public async handleItemCreated(data: ItemCreatedPayload): Promise<void> {
    await this.items.findOrCreate({
      where: { id: data.item.id },
    });
  }

Error:
Property 'id' does not exist on type 'Pick '.

Now for the second and final question. Does anyone know what the correct type here should be in order to get rid of Index signature for type 'string' is missing in type 'item'. and Property 'id' does not exist on type 'Pick '. errors? I am unable to fix both at the same time.

export class ItemCreatedPayload {
  constructor(
    public readonly item: ???????????,
  ) {
    const schema = Joi.object({ item: ItemCreatedSchema.required() });
    const { error } = schema.validate({ item }, { allowUnknown: true });

    if (error) {
      throw error;
    }
  }
}

I am aware that after typescript 4.3.0 the KnownKeys type is always never and I have tried every answer in this thread without success.

It's hard to coherently represent this problem in the playground, but here it is

Anyway, thanks for your time. Any feedback would be appreciated.

0 Answers
Related