typescript

using "as keyof interface" to convert string to key results in error if there is a readonly in the interface


So I have this function...

    updateField ( tenantId: string, fieldname: string, value: string | number | boolean | Array<SubTenant> | undefined) {
      const tenantPartial = {} as Partial<Tenant>;
      if(fieldname != "id"){
              tenantPartial[fieldname as keyof Tenant] = value as any;
      }

      this.buildingService.updateTenantField(this.buildingId, tenantId, tenantPartial)
    }

that uses this interface (simplified to what matters for the question)

export interface Tenant{
  name: string;
  readonly id: string
}

and fails with this error :

Cannot assign to 'id' because it is a read-only property.

(parameter) fieldname: string

As you can see, I tried eliminating the possibility that 'id' is used as a keyof Tenant, but that doesn't clear the error. How do I clear this error?


Solution

  • The problem is that your fieldName is declared as a string, so != 'id' does not narrow it - Exclude<string, 'id'> is still string, TypeScript does not support negated types (see Type for "every possible string value except ...").

    You can either change your parameter to keyof Tenant, which is basically 'id' | 'name', then that union will be narrows to just 'name' by your if statement:

    updateField (fieldname: keyof Tenant, value) {
      const tenantPartial = {} as Partial<Tenant>;
      if (fieldname != "id") {
         tenantPartial[fieldname] = value;
      }
    }
    

    or if you want to keep the type assertion, you need express the elimination of 'id' yourself:

    updateField (fieldname: string, value) {
      const tenantPartial = {} as Partial<Tenant>;
      if (fieldname != "id") {
         tenantPartial[fieldname as Exclude<keyof Tenant, 'id'>] = value;
      }
    }