Search code examples
typescriptindexingtypessignature

Typescript index signature failure Angular objects


I have the following angular typescript code.

In the function 'blendBinA' keep getting the error:

"Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'ObjA'.ts(7053)"

How to fix ?

interface ObjA {
    propA: string;
    propB: number;
    propC: string[];
}

public objA: ObjA = { propA: 'Henk', propB: 23, propC: ['singel 23'] };
public objB: any = { propX: '6445 GB', propA: 'henk' };

blendBinA() {
    for (const k in this.objB) {
        if (this.objA.hasOwnProperty(k)) {
            this.objA[k] = this.objB[k];
        }
    }
}

Solution

  • This is because this.objA.hasOwnProperty(k) does not acts like a type guard.

    You need to define your own custom typeguard:

    const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
      : obj is Obj & Record<Prop, unknown> =>
      Object.prototype.hasOwnProperty.call(obj, prop);
    

    and then try:

    interface ObjA {
      propA: string;
      propB: number;
      propC: string[];
    }
    
    
    const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
      : obj is Obj & Record<Prop, unknown> =>
      Object.prototype.hasOwnProperty.call(obj, prop);
    
    class Foo {
    
      public objA: ObjA = { propA: 'Henk', propB: 23, propC: ['singel 23'] };
      public objB: any = { propX: '6445 GB', propA: 'henk' };
    
      blendBinA() {
        for (const k in this.objB) {
          if (hasProperty(this.objA, k)) {
            this.objA[k] = this.objB[k];
          }
        }
      }
    }
    

    Playground

    If it possible, try to type objB

    P.S. It is possible to force hasOwnProperty to behave like typeguard:

    See this example:

    type Tag = { [prop: `tag${number}`]: never }
    
    interface Object {
      hasOwnProperty(v: PropertyKey): boolean & Tag
    }
    
    interface CallableFunction extends Function {
      call<
        T,
        Prop extends string,
        R extends boolean & Tag
      >(this: (this: T, property: Prop) => R, thisArg: T, property: Prop): thisArg is T & Record<Prop, string>;
    }
    
    declare const obj: { name?: string, surname?: number }
    
    if (Object.prototype.hasOwnProperty.call(obj, 'name')) {
      const test = obj.name // string
    }
    
    if (Object.prototype.propertyIsEnumerable.call(obj, 'name')) {
      const test = obj.name // string | undefined
    }
    

    Here you can find related question and answer with more explanation.

    Here you will find my article with some examples