3

I am getting various different data structures from external sources. In get() i am checking for undefined and existence of the property, but still getting compiler errors.

Is there a quick fix for that as we are just prototyping at the moment ?

class MyCache {
  private map: Map<string, object>;

  constructor() {
    this.map = new Map<string, object>();
  }

  populateMap(uuid: string): boolean {
    // ... getting a JSON object from somewhere
    let externalData: object = {};
    if (uuid == 'abc...') {
      externalData = {"bar": "BAR", "session": uuid}
    } else if( uuid == "def...") {
      externalData = {"foo": "FOO", "session": uuid}
    // ... some more else ifs ...
    } else {
      externalData = {"baz": "BAZ", "session": uuid}
    }
    this.map.set(uuid, externalData);
    console.log(externalData);
    return true;
  }

  get(uuid: string) {
    let response = this.map.get(uuid) || {'session': null};
    if (response.hasOwnProperty('session')) {
      return response.session;
    }
    return '';
  }
}

Error:

Property 'session' does not exist on type 'object'.
Maurice Meyer
  • 17,279
  • 4
  • 30
  • 47
  • Declare interface for `externalData`, if this is dynamic then you can declare it as `let externalData: { [key: string]: any } = {};` – Sameer Aug 16 '21 at 11:28

2 Answers2

2

AFAIK, type object is too general. I think it is better to use Record<string, unknown>. Please see ts eslint rule:

Unsafe:

//bad
const lowerObj: object = {};

Safe:

// good
const lowerObj: Record<string, unknown> = {};

Using hasOwnProperty is ok, but using Object.prototype.hasOwnProperty.call(foo, "bar") is much better. See eslint rule.

Here you can find recent optimization of V8 engine regarding using hasOwnProperty.

You can find more typings for hasOwnProperty in my article

Hence, this code should be ok:

class MyCache {
    private map: Map<string, Record<string, unknown>>;

    constructor() {
        this.map = new Map<string, Record<string, unknown>>();
    }

    populateMap(uuid: string): boolean {
        // ... getting a JSON Record<string, unknown> from somewhere
        let externalData: Record<string, unknown> = {};
        if (uuid == 'abc...') {
            externalData = { "bar": "BAR", "session": uuid }
        } else if (uuid == "def...") {
            externalData = { "foo": "FOO", "session": uuid }
            // ... some more else ifs ...
        } else {
            externalData = { "baz": "BAZ", "session": uuid }
        }
        this.map.set(uuid, externalData);
        console.log(externalData);
        return true;
    }

    get(uuid: string) {
        let response = this.map.get(uuid) || { 'session': null };
        if (response.hasOwnProperty('session')) {
            return response.session;
        }
        return '';
    }
}

Playground

1
  1. Use Object instead of object link
  2. Use 'key' in obj instead of hasOwnProperty link
class MyCache {
    private map: Map<string, Object>;

    constructor() {
        this.map = new Map(); // No need for generic parameters, type is already set in the property declaration
    }

    get(uuid: string) {
        let response = this.map.get(uuid) || { 'session': null };
        if ('session' in response) {
            return response.session;
        }
        return '';
    }
}
Roberto Zvjerković
  • 9,657
  • 4
  • 26
  • 47