Dexie.js: typescript insanity

Created on 23 Feb 2019  ·  10Comments  ·  Source: dfahlander/Dexie.js

So let's start with a working example:

import Dexie from 'dexie';

class Database extends Dexie {
    //config!: Dexie.Table<{ id: string }, string>

    constructor() {
        super("db9");
        this.version(1).stores({
            config: 'id',
        });
    }
}

const db = new Database();
console.log("db.config: ", (db as any).config);

Which prints the Table obj. However (db as any).config is pretty unergonomic, so let's make a tiny change an insert a typing:

import Dexie from 'dexie';

class Database extends Dexie {
    config!: Dexie.Table<{ id: string }, string>

    constructor() {
        super("db9");
        this.version(1).stores({
            config: 'id',
        });
    }
}

const db = new Database();
console.log("db.config: ", (db as any).config);

So now we can use Database pretty ergonomically, but now db.config is undefined (?!!). Ok, no problem: let's compare the generated js and see what's going on. Nope. typescript generates the same javascript. WTF?! I think I've lost my sanity. Any ideas?

(For what it's worth, this happens in the context of a ts create-react-app, I can build a mini example if it's not easy to reproduce in other contexts)

Most helpful comment

The TypeScript example is broken due to this issue with Babel, and it was confusing to debug, because on the surface everything seems correct.

… and it has nothing to do with Dexie, so i'll close this issue.

It affects users so it does have something to do with Dexie. This issue should be renamed to make it discoverable, and the example should be updated. The proper solution is also to use declaration merging:

export class Db extends Dexie {
  constructor() {
    super('test');

    this.version(1).stores({
      posts: `link, user, date`,
    });
  }
}

export interface Db {
  posts: Dexie.Table<Post, string>;
}

This works as expected and doesn't require a workaround like assigning the properties manually.

All 10 comments

So there is indeed a slight difference in the actual javascript that runs in a browser, the version that works runs:

    return Object(v.a)(this, t),
      (e = Object(b.a)(this, Object(k.a)(t).call(this, "db9"))).version(1).stores({
          config: "id"
      }),
      e

while in the failure case, this is getting executed

    return Object(v.a)(this, t),
      (e = Object(b.a)(this, Object(k.a)(t).call(this, "db9"))).config = void 0,
      e.version(1).stores({
          config: "id"
      }),
      e

👋 Hi @madeken, I'm also stumbling through the Typescript examples right now. Not sure if this is the "correct" way, but I was able to get a working version with a DB class similar to:

export class MyDB extends Dexie {
  boards: Dexie.Table<IBoardEntity, number>;

  constructor() {
    super('MyDB_Name');

    this.version(1).stores({
      boards: "++id,createdAt,updatedAt,title"
    });

    this.boards = this.table("boards");
  }
}

Ok, so I think the problem is that typescript[1] is effectively generating code like this:

constructor() {
        super("db9");
        this.config = undefined; // <--- here is the problem
        this.version(1).stores({
            config: 'id',
        });
}

Then the call to .stores() is adding a config to the prototype object, but it's overshadowed by the undefined assignment.

[1] When I use tsc to compile the file, it doesn't inject that this.config = undefined assignment, so it's a bit confusing. I don't really understand the whole architecture, so maybe it's a problem caused by babel?

I've made a minimal example:

class Pimped  {
    pi!: number;
    constructor() {
        Object.setPrototypeOf(this, {
            pi: 3.14
        });
    }
}

const p = new Pimped();
console.log('p.pi is: ', (p as any).pi);

and it has nothing to do with Dexie, so i'll close this issue.

That's interesting news for me. Tried to reproduce but I don't get typescript compiler (tsc) to emit the line that sets the table props to undefined. Tried with various tsconfigs. May it be create-react-app that does some special transformation here?

Yeah, I created an issue here: https://github.com/facebook/create-react-app/issues/6506

I'm guessing it's something to do with babel

I've tried create-react app --typescript and it seems to be compiling typescript using babel and not tsc. This option is is described in this blog. I suppose the babel plugins included in "babel-preset-typescript" that also shares a lot with flow, is the one that makes the decision to treat class properties marked with exclamation mark, as being equivalent to @babel/proposal-class-properties, that would probably treat them as being set to undefined unless followed by "= someValue".

Anyhow, we should expect more people to be using babel to compile typescript, so I think the docs needs to be updated.

Want to change from this recommentation:

class Database extends Dexie {
  friends!: Dexie.Table<Friend, number>;

  constructor() {
    super("friendsdb");
    this.version(1).stores({
      friends: '++id, name, age'
    });
  }
}

Into this (that would work in all cases):

class Database extends Dexie {
  friends: Dexie.Table<Friend, number>;

  constructor() {
    super("friendsdb");
    this.version(1).stores({
      friends: '++id, name, age'
    });
    this.friends = this.table("friends");
  }
}

makes sense, that looks good 👍

The TypeScript example is broken due to this issue with Babel, and it was confusing to debug, because on the surface everything seems correct.

… and it has nothing to do with Dexie, so i'll close this issue.

It affects users so it does have something to do with Dexie. This issue should be renamed to make it discoverable, and the example should be updated. The proper solution is also to use declaration merging:

export class Db extends Dexie {
  constructor() {
    super('test');

    this.version(1).stores({
      posts: `link, user, date`,
    });
  }
}

export interface Db {
  posts: Dexie.Table<Post, string>;
}

This works as expected and doesn't require a workaround like assigning the properties manually.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asdip138 picture asdip138  ·  3Comments

matusferko picture matusferko  ·  4Comments

fulltic picture fulltic  ·  4Comments

oviniciuslara picture oviniciuslara  ·  4Comments

dfahlander picture dfahlander  ·  4Comments