Something annoying about promises


#1

Here’s something I noticed when coding in Visual Studio Code and returning an object from a promise.
Suppose I have the following function…

getSomething()
  {
    return new Promise(function(resolve, reject)
    { firebase.database().ref('something').once('value')
      .then((req)=>{resolve(req.val())})
      .catch((err)=>{reject(err)})
    });
  }

and it returns the JSON object

{
id:1,
name:'Bob'
}

Then in another page, I call the function like this…

getSomething().then((c)=>{
console.log(c.name);//red lines appear here, showing "property 'name' does not exist on type {}"
})

The code runs fine and displays the name, but VS Code shows a red line under the “c.name”, and mousing over it, it says property ‘name’ does not exist on type {}.

I find it annoying because I don’t want any red lines on any of my code pages.

The only way I could resolve this is to define another variable and assign it to c, then access the attributes from it. In other words…

var obj;// The variable

getSomething().then((c)=>{
obj = c;
console.log(obj.name);//no errors!!
})

Any idea why this is so? Is there a better way to code this?

Thanks.


#2

I don’t have VS Code but you could probably solve that by defining and giving a type for c

like

export interface Something {
      name: string;
 }

 getSomething().then((c: Something)=>{

or you could too using classes

export class Something {
   name: string;
}

also in your getSomething method

getSomething(): Promise<Something> {
  ....
}

or in worst case use any

 getSomething().then((c: any)=>{

#3

The last one (C: any) worked. Thanks!


#4

The point of the red lines is to warn you when you’re making a mistake. Getting rid of them with any completely defeats the purpose of having typing in the first place.


#5

Hello,

typescript brings types to javascript. Using variables with out decaring and typing (or as any) is imho a dirty way, it brings pain if something not working as expected,

Maybe you should got the first way, In long term life is much easier.

Best regards, anna-liebt


#6

Cool nice to hear any is the reason. But note, it’s a general bad practice to use any respectively it would be better to define a type like describe above with interface or classes in order to avoid bugs and problem on the long term


#7

If u want to do the improper properly, do:

c['name']

Then u dont need any either

:grinning::grinning::grinning:


#8

So the general consensus is to define and use classes/type, rather than generically use ‘any’. Thanks, I’m still getting used to Typescript. So how do you handle custom types? Do you create them in a subfolder inside /src/ and then import in respective pages?


#9

Declaring them alongside whatever service provider provides them is probably most conventional:

export interface Person {
  name: string;
}

@Injectable() export class PersonService {
  allPeople(): Observable<Person[]> { ...}
}

Additionally, if you do insist on using a Promise instead of an Observable, do not explicitly instantiate it as you are. Instead use the toPromise operator.


#10

@obinnae like @rapropos explained, you could declare your class or interfaces in any providers. But you could also declare them in separate file, as you want. For example, in my app I have something like

src/providers/model

where I declare my custom objects. For example let say a model for a user, I will then have

src/providers/model/user/user.ts

which contains something like

export interface User {
   id: string;
}

this object could then be use like

 this.something().then((user: User) => {});

or declared in a page for example

export class MyPage() {
   
   user: User;

}

if you want to user class instead of interfaces that also possible, the advantage is that you could use constructor then, for example

export class User {
   id: string;
  
  constructor() {
    this.id = 'default';
  }
}

which could be then use like

 let user: User = new User();
 console.log(user.id); // -> 'default'

#11

Thanks, @reedrichards. Makes sense. I’ll implement it like that.
I’m basically representing objects stored in firebase db. So if I have a Firebase user object like so

\Users\user1\{id:1, name:'Richard'},

in the code I can do something like

var user:User;

firebase.database().ref('users/user1').once('value', (snapshot)=>{
 user = snapshot.val();
 console.log(user.name);
});

#12

Hi @rapropos, I’ll look into observables, as I’m more familiar with Promises than Observables.


#13

Observables are promises on steroids

So be carefull handling


#14

Reed, I’m trying to decide whether to use interfaces or classes (or both). Here’s what I’m aiming at…

export class User
{
 id: string;
 name: string;

  constructor(id: string = null)
  { if (id)
     // return User from Firebase where id=id
    else
    // return empty User object
  }

  save(usr:User)
  {
  // save new user to Firebase
  }

  update(id: string, userObj: User)
  {
    // Update Firebase user with userObj where id=id
  }

  delete(id: string)
  {// Delete User where id=id
  }
}

then use it in other pages like so…

var usr:User = new User(6) // to return user whose ID=6

Is that about right, or you have a better solution (perhaps with interface and/or observables)? Sorry I’m still mastering angular/typescript.


#15

Thanks Tom, I’ll keep it in mind.


#16

In angular it normally works out better to have data as a PODO, and then have the actual logic inside of Providers.

For a more concrete example of this, going based on your class example you don’t really have any way of getting a reference to the Firebase object unless you pass it to all of the functions.

So I’d define an interface

interface User {
  id: string;
  name: string;
}

Then create a provider that retrieves & manages them.


#17

@obinnae1h class or interface in my point of view it’s up to you or up to your use case

BUT, I would advice to avoid doing return in a constructor. Furthermore, only my point of view, I think it’s not that readable to have a promise/observable initialization in a constructor, I would rather keep the definition as easy and simple as possible