2

I'm using Knex.js with TypeScript for database access.

Table

  • uid (UUID, auto-generated by Postgres)
  • name (varchar)

Model

    interface User {
        uid: string | null;
        name: string;
    }

Repository

    class UserRepository {
        public async insert(user: User): Promise<void> {
            await knex('user').insert(user);
        }
    }

Usage

function someRouteHandler(req, res) {
    const repo = new UserRepository(...)

    // Next line will error out due to the object literal
    // not having the uid property
    repo.insert({
        //uid: null // satisfies interface constraint but errors during insert
        name: req.body.name,
    });
}

Problem

The only way I see this working is to modify the repository class methods to explicitly say which fields I need to insert:

public async insert(user: User): Promise<void> {
    await knex('user').insert({
        name: user.name,
    });
}

Unfortunately, this adds maintenance efforts since every time I modify the model I also need to modify the methods that do the queries to reflect the changed properties.

Is there a cleaner way of doing this?

2
  • 1
    What is the actual value of the uid you're populating the property with? If it's a UUID, you could generate the UUID in the application layer and pass the value to the insert method, instead of passing null.
    – Andy
    Commented Jul 28, 2020 at 6:33
  • Have you tried with undefined instead of null? In any case, as far as I can tell this has nothing to do with TS itself, it's a quirk of the library
    – gcali
    Commented Aug 27, 2020 at 7:34

3 Answers 3

2

As @Andy wrote in the comment, you could move the creation of the UUID from the Database into the application layer. To tell the truth, i would stick with the database generation, its less effort and its more failsafe.

You could try a two interfaces approach

interface MyModel {
  name: string;
}

interface MyModelDatabase extends MyModel {
  uuid: string;
}

Then you can use MyModel when you insert and MyModelDatabase when you work with querys from the database.
Be aware that i omit the "null" at the UUID in the interface. The reason is, that your database will always generate that uuid, therefor it will never be null.

0

The best way I know is, you can create an alias of that property at the beginning of the method or at initialization and whenever you make changes in your model, you will just have to make changes in that alias property.

0

The problem is you use the same data model for different purposes. You need to have an insert version of the model and a normal version of the model so you don't need implicit uuid constraint where it's null during creation but not after it.

With this you can just pass the insert version to DB without any issues and have better type safety.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.