Richard Richard - 1 month ago 14
TypeScript Question

Typescript chaining Promises

If anyone can help with the following Typescript Error, I would appreciate it.

Thanks.

I am making use of the following code where it has the following. It needs to be able to chain the

Promise
.

line 26: dbPromise = _db.execute(sql)


When I try build it, I get the following:


ERROR in ./app/pages/chats/SqlDatabase.ts
(26,9): error TS2322: Type 'Promise<SqlResultSet>' is not assignable to type 'Promise<SqlDatabase>'.
Type 'SqlResultSet' is not assignable to type 'SqlDatabase'.
Property '_db' is missing in type 'SqlResultSet'.



I am using
Typescript version 2.0.3.


code:

import { isBrowser } from './platform';
import { SqlResultSet } from './SqlResultSet';

export class SqlDatabase {

constructor(private _db: any) { }

static open(name: string, initStatements: string[] = []): Promise<SqlDatabase> {
let dbPromise = isBrowser()
.then(browser => {
const openDatabase = browser ? openBrowserDatabase : openCordovaDatabase;
return openDatabase(name);
});
if (initStatements.length === 0) {
return dbPromise;
}
let _db: SqlDatabase;
// execute the first statement and capture the _db
dbPromise.then(db => {
_db = db;
return db.execute(initStatements.shift());
});
// execute all the other statements (if any) sequentially
for (let sql of initStatements) {
dbPromise.then(() => {
dbPromise = _db.execute(sql)
});
}
// resolve the _db only after all statements have completed
return new Promise((resolve, reject) => {
console.log('resolve: ', resolve);
dbPromise.then(() => resolve(_db)).catch(reject);
});
}

execute(statement: string, params: any[] = []): Promise<SqlResultSet> {
console.log('execute: ' + statement);
return new Promise((resolve, reject) => {
this._db.transaction(tx => tx.executeSql(statement, params, (tx, resultSet) => {
console.log('execute: resolve: ', resultSet);
resolve(resultSet);
}, (tx, error) => {
reject(error)
}));
});
}
}

declare var sqlitePlugin: any;

function openCordovaDatabase(name: string): Promise<SqlDatabase> {
return new Promise((resolve, reject) => {
if (typeof sqlitePlugin === 'undefined') {
reject(new Error('[ionix-sqlite] sqlitePlugin global object not found; did you install a Cordova SQLite plugin?'));
}
const db = sqlitePlugin.openDatabase({
name: name,
location: 'default'
});
console.info('[ionix-sqlite] using Cordova sqlitePlugin');
resolve(new SqlDatabase(db));
});
}

declare function openDatabase(name: string, version: string, desc: string, size: number): any;

function openBrowserDatabase(name: string): Promise<SqlDatabase> {
return new Promise((resolve, reject) => {
try {
const db = openDatabase(name, '1.0', name, -1);
console.info('[ionix-sqlite] using WebSQL');
resolve(new SqlDatabase(db));
} catch (error) {
reject(error);
}
});
}


UPDATE

Thank you everyone for your advise below. You have located that the problem was on line:

dbPromise = _db.execute(sql)

trying to change a
Promise<SqlDatabase>
to a
Promise<SqlResultSet>


Apologies, but I am not a javascript/typscript expert, so excuse the basic questions.

I have changed the code to the following, but it still has issues, I would appreciate it if anyone could advise.

As you can see in the
open
function, I split it into two separate
Promise
s (
Promise<SqlDatabase>
and
Promise<SqlResultSet>
). The
open
function works when invoked and the sql statements are executed.

SqlDatabase.ts

static open(name: string, initStatements: string[] = []): Promise<SqlDatabase> {
let dbPromise: Promise<SqlDatabase> = isBrowser()
.then(browser => {
const openDatabase = browser ? openBrowserDatabase : openCordovaDatabase;
return openDatabase(name);
});
if (initStatements.length === 0) {
return dbPromise;
}

let _db: SqlDatabase;
// execute the first statement and capture the _db
dbPromise.then(db => {
_db = db;
return db.execute(initStatements.shift());
});

// execute all the other statements (if any) sequentially
for (let sql of initStatements) {
dbPromise.then(() => {
let resultPromise: Promise<SqlResultSet> = _db.execute(sql)
});
}

// resolve the _db only after all statements have completed
return new Promise((resolve, reject) => {
console.log('resolve: ', resolve);
dbPromise.then(() => resolve(_db)).catch(reject);
});
}


My problem is when the
execute
function is invoked, it just freezes.

chatsStorageService.ts

private dbPromise: Promise<SqlDatabase>;
private resultPromise: Promise<SqlResultSet>;

public refreshChats(): Promise<any> {
const statement: string = "SELECT * FROM chats";
return this.dbPromise /// <== should be resultPromise
.then((db) => db.execute(statement))
.then((resultSet) => {
this.chats = [];
if (resultSet.rows.length > 0) {
for (var i = 0; i < resultSet.rows.length; i++) {
this.populateChat(resultSet.rows.item(i));
}
}
});
}


As you can see, I am not sure how to instantiate the
Promise<SqlResultSet>
.

Answer

The error is due to the fact that you try to change the type of dbPromise.
When it is first declared:

let dbPromise = isBrowser()
      .then(browser => {
        const openDatabase = browser ? openBrowserDatabase : openCordovaDatabase;
        return openDatabase(name);
      });

the compiler refers the type to Promise<SqlDatabase> (based on the error), but then you try to assign something else:

dbPromise = _db.execute(sql)

which is of type Promise<SqlResultSet>.
You can solve it like so:

let dbPromise: Promise<any> = ...

Or you can have two different promise variables (i.e.: dbPromise: Promise<SqlDatabase> and resultPromise: Promise<SqlResultSet>) which sounds better to me.

Comments