Home > Back-end >  Issue in typescript - Property in type is not assignable to the same property in base type
Issue in typescript - Property in type is not assignable to the same property in base type

Time:09-27

I have a generic interface defined as

export interface EventInput {
    eventName: string;
    metadata?: Record<string, string>;
    payload?: Record<string, unknown>;
}

and I create a class

import {EventInput, EventUtilsService} from "@learnapp-co/la-event-utils";

import {UpdateOrganisationInterface} from "./interface";

export interface OrganisationUpdatedPayload {
  orgId: string;
  pii: string;
}

export class OrganisationUpdatedEventInput implements EventInput {
  private readonly eventName = 'OrganisationUpdated';

  private readonly payload: OrganisationUpdatedPayload;

  constructor(orgId: string, body: UpdateOrganisationInterface, secret: string) {
    this.payload = {
      orgId,
      pii:  EventUtilsService.encryptPIIData({
        organisationName: body.name,
        bank: body.bank,
        ...(body.revenuePercentage && { revenuePercentage: body.revenuePercentage}),
      }, secret),
    }
  }

  public get getEventName(): string {
    return  this.eventName
  }

  public  get getPayload(): OrganisationUpdatedPayload {
    return this.payload
  }
}

But I am getting the error

TS2416: Property 'payload' in type 'OrganisationUpdatedEventInput' is not assignable to 
the same property in base type 'EventInput'.   Type 'OrganisationUpdatedPayload' is not 
assignable to type 'Record<string, unknown>'. Index signature is missing in type 
'OrganisationUpdatedPayload'.

NOTE: - There can be as many classes as possible for eg OrganisationCreatedeventInput with a different payload which will extend EventInput class.

How can we solve this issue?

CodePudding user response:

You're stating that OrganisationUpdatedEventInput must implement EventInput that has a payload property with a broad type.

Then you declare a different (narrowed) type directly on OrganisationUpdatedEventInput. This is not allowed as you can see in your error.

Typescript is telling you that you either need to narrow down the payload property on EventInput or allow this broader type in OrganisationUpdatedEventInput.

I would suggest that you either use generics: interface EventInput<T> as mentioned by @Yurii Panasenko:

Or use the broader type in OrganisationUpdatedEventInput and narrow it down in the getter getPayload with a safety check to make sure it's the correct type.

Also you're declaring properties in EventInput interface as public (as interfaces do) and then declare them private in OrganisationUpdatedEventInput. This is also not supported by TypeScript and doesn't really make sense.

If you want to do things this way, it may be better to just use inheritance. Make a base class EventInput and extend OrganisationUpdatedEventInput from this class. Something like this:

export abstract class EventInput<T> {
    protected abstract readonly eventName: string;
    protected readonly payload?: T;
    protected metadata?: Record<string, string>;

    constructor(payLoad?: T) {
        this.payload = payLoad;
    }

}

export interface OrganisationUpdatedPayload {
    orgId: string;
    pii: string;
}

export class OrganisationUpdatedEventInput extends EventInput<OrganisationUpdatedPayload> {
    protected readonly eventName = 'OrganisationUpdated';

    constructor(orgId: string, pii: string) {
        super({ orgId, pii });
    }

    public get getEventName(): string {
        return this.eventName;
    }

    public get getPayload(): OrganisationUpdatedPayload | undefined {
        return this.payload;
    }
}

CodePudding user response:

You can try to use T parameter

export interface EventInput<T> {
    eventName: string;
    metadata?: Record<string, string>;
    payload?:  T;
}

export class OrganisationUpdatedEventInput implements EventInput<OrganisationUpdatedPayload> {
  private readonly eventName = 'OrganisationUpdated';

  private readonly payload: OrganisationUpdatedPayload;

  constructor(orgId: string, body: UpdateOrganisationInterface, secret: string) {
    this.payload = {
      orgId,
      pii:  EventUtilsService.encryptPIIData({
        organisationName: body.name,
        bank: body.bank,
        ...(body.revenuePercentage && { revenuePercentage: body.revenuePercentage}),
      }, secret),
    }
  }

  public get getEventName(): string {
    return  this.eventName
  }

  public  get getPayload(): OrganisationUpdatedPayload {
    return this.payload
  }
}
  • Related