error TS2339: Property 'x' does not exist on type 'Y'

61

I don't understand why this code generates TypeScript error. (It's not the original code and is a bit derived, so please ignore the non-sense in the example):

interface Images {
  [key:string]: string;
}

function getMainImageUrl(images: Images): string {
  return images.main;
}

I'm getting error (using TypeScript 1.7.5):

error TS2339: Property 'main' does not exist on type 'Images'.

Of course I could get rid of the error when writing:

return images["main"];

I'd prefer not using string to access the property. What can I do?

This question is tagged with typescript

~ Asked on 2016-07-12 09:29:31

The Best Answer is


38

If you want to be able to access images.main then you must define it explicitly:

interface Images {
    main: string;
    [key:string]: string;
}

function getMainImageUrl(images: Images): string {
    return images.main;
}

You can not access indexed properties using the dot notation because typescript has no way of knowing whether or not the object has that property.
However, when you specifically define a property then the compiler knows that it's there (or not), whether it's optional or not and what's the type.


Edit

You can have a helper class for map instances, something like:

class Map<T> {
    private items: { [key: string]: T };

    public constructor() {
        this.items = Object.create(null);
    }

    public set(key: string, value: T): void {
        this.items[key] = value;
    }

    public get(key: string): T {
        return this.items[key];
    }

    public remove(key: string): T {
        let value = this.get(key);
        delete this.items[key];
        return value;
    }
}

function getMainImageUrl(images: Map<string>): string {
    return images.get("main");
}

I have something like that implemented, and I find it very useful.

~ Answered on 2016-07-12 09:50:46


25

The correct fix is to add the property in the type definition as explained in @Nitzan Tomer's answer. If that's not an option though:

(Hacky) Workaround 1

You can assign the object to a constant of type any, then call the 'non-existing' property.

const newObj: any = oldObj;
return newObj.someProperty;

You can also cast it as any:

return (oldObj as any).someProperty;

This fails to provide any type safety though, which is the point of TypeScript.


(Hacky) Workaround 2

Another thing you may consider, if you're unable to modify the original type, is extending the type like so:

interface NewType extends OldType {
  someProperty: string;
}

Now you can cast your variable as this NewType instead of any. Still not ideal but less permissive than any, giving you more type safety.

return (oldObj as NewType).someProperty;

~ Answered on 2017-07-10 16:23:45


Most Viewed Questions: