Typescript Question / Issue

Im trying to convert all my js files into typescript, from what I understand that is what will be supported moving forward.
Im getting Property ‘res’ does not exist on type ‘{}’. with this line : if(data.res.rows.length > 0) and the same for an http service, Property ‘status’ does not exist on type ‘{}’. Im not sure what Im missing? The compiler doesn’t like it =(.

For the first code example Im calling my service as such:

this.sql.checkDuplicate(this.loginForm.value.username).then(data => {
  console.log(data);
  if(data.res.rows.length > 0){

Here is what Im seeing in ionic serve:

Error TS2339: Property ‘res’ does not exist on type ‘{}’.

When logging to console the data is correct, but compiler says nay. It has to be a typing issue.

Any help would be appreciated.

This is what Im seeing in my editor, just to clarify a little more.

If you type data to data:any the compiler is happy, but you are not getting any benefits from using TS.

What you need to define is the structure of your data variable.

Chris

Well I would agree that it should be happy with data:any, but not even that is making it happy :frowning:
Ill post my entire Component here, maybe there is something else.

import { Component } from '@angular/core';
import { NavController, Events } from 'ionic-angular';
import {FormBuilder,Control,ControlGroup,Validators} from '@angular/common';

import { Config } from '../../providers/config/config';
import { Rest } from '../../providers/rest/rest';
import { Sql } from '../../providers/sql/sql';


@Component({
  templateUrl: 'build/pages/register/register.html',
})
export class Register {
  loginForm : ControlGroup;
  data: any;

  constructor(private nav: NavController, formBuilder: FormBuilder,private events:Events, private sql:Sql, private rest:Rest) {
    this.loginForm = formBuilder.group({
      username:['bleh',Validators.required],
      password:['blah',Validators.required]
    });

  }
  cancel() {
    this.nav.pop();
  }

  login() {
    event.preventDefault();

    this.sql.checkDuplicate(this.loginForm.value.username).then(data => {
      console.log(data);
      if(data.res.rows.length > 0){
        //console.log(data.res.rows.length + ' accounts found.')
        alert('The account ' + this.loginForm.value.username + ' is already added as a profile. Please use a different account.')
      }
      else{

        let tmpCredentials = {
          user:
          {
            'account': this.loginForm.value.username,
            'password': this.loginForm.value.password
          }
        };

        this.rest.authenticate(JSON.stringify(tmpCredentials)).then(data => {
          if(data.status == 200){
            this.sql.addProfile(data.res.account[0]).then(data => {
              if(data.res.rowsAffected) {
                this.events.publish('reloadProfiles');
                this.nav.pop();
              }
            });
          }
          if(data.status == 401){
            console.log(data)
          }
        });

      }
      
    })

  }
}

There are a couple of problems here. First, the data property of Register has nothing to do with the data in the login() method. If you’re coming from a Java background, it is important to always remember that using this. to access instance variables is absolutely mandatory, always.

Secondly, the return value of checkDuplicate in the sql provider is probably wrong. It should be Promise<Something> where Something has the res property you’re trying to use.

Here is what I would do based on your screenshot, I mean if you want to really normalize the interfacing.

authenticate(...): Promise<IAuthResult> {

}

also

export interface IAuthResult {
  status: number,
  res: any,
  ...
}

I’d also say when defining the interface, keep it lean so that you didn’t end up with a verbose definition that’s probably intuitive and not easy to maintain.

Prefixing interfaces with “I” is explicitly discouraged in the TypeScript handbook, FWIW.

Thanks for the replies! Unfortunatley I don’t come from a Java background, so the OO thing is kinda new to me. Im trying to educate myself more each day!

@rapropos Does this not work the same in typescript for a promise? This is what Im using in the sql provider … it worked as js before converting to ts.

  checkDuplicate(email) {
    return new Promise(resolve => {
      this.storage.query("SELECT email FROM accounts WHERE email = ?",[email]).then(data =>{
        resolve(data);
      }, (error) => {
        resolve(error);
        console.log('QUERY DUPLICATE FAILED --> ' + JSON.stringify(error.err))
      })
    })
  }

@itlr Is is possible to see a little more of how to use the example you gave above? I would rather normalize, just need more of an example. Thanks!

I’m not sure what would happen when trying to infer a return type that may involve a generic, so I try to make a habit of always declaring return types for all functions.

checkDuplicate suffers from what is generally referred to as the explicit promise construction antipattern, and it’s treating errors as successes. It looks to me like the function is trying to check if an email is already in use, in which case I would do it this way (untested):

isDuplicate(email:string):Promise<boolean> {
  return this.storage.query("SELECT COUNT(*) AS nrecs FROM accounts WHERE email = ?", [email]).then((dbrv) => {
    // sanity check on dbrv.rows.length
    return dbrv.rows.item(0).nrecs > 0;
  });
  // failures are propagated to caller
}

@rapropos It’s a matter of preference using ‘I’ or not, personally I tend to omit most of the time myself. I just wanted to make the answer a bit more explicit.

@relevantidea, I encourage you to take a look at this talk.

The syntax might look a bit verbose, but looking closer it’s really not, just providing typing that’s all

authenticate(..): Promise<IAuthResult) {

}

authenticate will return a Promise, what kind of Promise you ask? Well a Promise that emits an IAuthResult interface. What’s an IAuthResult interface, well nothing special, it just defines what is expected in the http call payload.

When the official coding guidelines say “certain conventions apply to how idiomatic TypeScript code is written”, and bothers to include as one of them “Do not use “I” as a prefix for interface names”, to me that goes beyond a simple “matter of preference”.

an official coding guidelines is not a religious script, I believe me as a developer shall have a choice and so is the team I’m working with, sometimes it helps others it doesn’t. Like said I did acknowledge the un-necessity of “I” in many cases, but I tend to think of it as a choice not a rule. Let’s not assert that it has to be, coz it is not - to me at least.