Typescript : Property does not exist on type 'object'
Asked Answered
D

4

149

I have the follow setup and when I loop through using for...of and get an error of :

Property "country" doesn't exist on type "object".

Is this a correct way to loop through each object in array and compare the object property value?

let countryProviders: object[];

export function GetAllProviders() {
   allProviders = [
      { region: "r 1", country: "US", locale: "en-us", company: "co 1" },
      { region: "r 2", country: "China", locale: "zh-cn", company: "co 2" },
      { region: "r 4", country: "Korea", locale: "ko-kr", company: "co 4" },
      { region: "r 5", country: "Japan", locale: "ja-jp", company: "co 5" }
   ]

   for (let providers of allProviders) {
      if (providers.country === "US") { // error here
         countryProviders.push(providers);
      }
   }
}
Decrease answered 11/4, 2017 at 6:55 Comment(0)
L
156

You probably have allProviders typed as object[] as well. And property country does not exist on object. If you don't care about typing, you can declare both allProviders and countryProviders as Array<any>:

let countryProviders: Array<any>;
let allProviders: Array<any>;

If you do want static type checking. You can create an interface for the structure and use it:

interface Provider {
    region: string,
    country: string,
    locale: string,
    company: string
}

let countryProviders: Array<Provider>;
let allProviders: Array<Provider>;
Lactoscope answered 11/4, 2017 at 7:39 Comment(9)
Why you didn't use a class instead an interface?Clarisclarisa
@RafaelReyes If all you need is compile-time type checking on your objects, you can use interfaces (interfaces are erased during build and don't affect the generated JS). Classes however do get converted to JS. Based on the question there was no need for a class to be created.Lactoscope
@Lactoscope how would you handle this issue if you are using third party package. I am using JSforce and trying to use "search" function on the object but it is throwing "Property 'search' does not exist on type 'Connection'"...any clues?Diatomite
@Diatomite Then it is probably a case of the type definitions not updated with the library. You can either add the method yourself and raise a PR in the DefinitelyTyped repo for a permanent fix (this will take some time). Or if you want to fix this temporarily, you can add properties to declarations as described hereLactoscope
But property country does exist on object - it's a string, JS object keys are strings. How can I tell TS to allow me to access an object like it's an object?Radloff
@Radloff then you should be using index types (or disable static typing with any) which support arbitrary keys, not object.Lactoscope
@Lactoscope But that's what object is for in JS. Surely TS supports using object to do what object has always done? Isn't the whole point of TS that existing JS should work?Radloff
@Radloff Think of object here as System.Object in C# or its equivalent in Java or other language. You can use the type to store any value, but to access its properties, you need to cast it to a specific type. And object is in no way a JS construct, it is a TS static type to indicate any non-primitive value. If you wan JS-like flexibility, then you are really looking for dynamic typing (which is what any offers). And your assertion is wrong that all valid JS code works with TS. See thisLactoscope
Let's not teach people to use any. It turns off type checking completely (which rather is not what they want if they're using TS in the first place) and is very hard to unwind if other code starts depending on the fact that there's some any. Just type countryProviders according to the shape its elements has: 4 keys it all it's got, from what I see. Or use the Record type if that's not the case.Persevere
S
56

If your object contains any key/value pairs, you could declare an interface called keyable like :

interface keyable {
    [key: string]: any  
}

then use it as follows :

let countryProviders: keyable[];

or

let countryProviders: Array<keyable>;

For your specific case try to define the key type and use Record utility to define the object :

type ProviderKey="region" | "country" | "locale" | "company"

let countryProviders: Array<Record<ProviderKey,string>>;

Squire answered 11/11, 2020 at 7:54 Comment(0)
A
14

Yes the above methods are highly recommended, but if you have no choice and want to continue using "allProviders" as an array of object, then go for this method. This worked for me without creating an interface.

if(providers["country"] === "US") // replacement
Antitrust answered 21/4, 2021 at 18:28 Comment(1)
How the field is accessed is unrelated to the issue: TypeScript playground – typescriptlang.org/play?ts=4.3.5#code/…Werra
C
8

Yes you should try to avoid object, any etc. if you can, after all the thing is called TypeScript for a reason.

If, however, you need a working dirty trick:

let o: object = { id: 1 }
console.log(o.id);                // throws error
console.log((o as any).id);       // fine

As always: anything goes with any.

Cheerio answered 23/6, 2023 at 7:28 Comment(1)
(o as any) is a lifesaver! I just need to use an external object and its methods in TypeScript without googling mysterious errors for hours or completely rewriting 2 huge projects to work together. Sometimes you just need a hack.Nervy

© 2022 - 2024 — McMap. All rights reserved.