Argument of type 'Function | FieldValue' is not assignable to parameter of type 'string'

1.1k Views Asked by At

I create a user model:

class UserModel extends Model {

    static table = 'users'
    static timestamps = true

    static fields = {
        id: { primaryKey: true, autoIncrement: true},
        firstName: DataTypes.STRING,
        lastName: DataTypes.STRING,
        username: DataTypes.STRING,
        email: DataTypes.STRING,
        password: DataTypes.STRING,
        birthday: DataTypes.DATE,
        phoneNumber: DataTypes.INTEGER,
    }
}

while I compare the existent user password with the new one:

async signin(user: Pick<User, "username" | "password">){
        const { username, password } = user;

    const existentUser = await UserModel.where('username', username).first()

    if (!existentUser) throw new CustomErrorHandler(Status.NotFound, "User does not exist")

    const isPasswordCorrect = await bcrypt.compare(password, existentUser.password); // Argument of type 'Function | FieldValue' // is not assignable to parameter of type 'string'.
 // Type 'null' is not assignable to type 'string'.
}

I got this ts error:

Argument of type 'Function | FieldValue' is not assignable to parameter of type 'string'.
  Type 'null' is not assignable to type 'string'.

I can fix it by forcing the type using :

const isPasswordCorrect = await bcrypt.compare(password, <string>existentUser.password);

but I'm looking for another solution. Is there another approach to convert returned Model by first() to User interface or something else?

1

There are 1 best solutions below

0
danopia On

In order to use typed fields on your models, you'll want to set up Model Records by duplicating the fields, like so:

class UserModel extends Model {
    static table = 'users'
    static timestamps = true

    static fields = {
        id: { primaryKey: true, autoIncrement: true},
        firstName: DataTypes.STRING,
        lastName: DataTypes.STRING,
        username: DataTypes.STRING,
        email: DataTypes.STRING,
        password: DataTypes.STRING,
        birthday: DataTypes.DATE,
        phoneNumber: DataTypes.INTEGER,
    }

    id!: string;
    firstName!: string;
    lastName!: string;
    username!: string;
    email!: string;
    password!: string;
    birthday!: Date;
    phoneNumber!: number;
}

Also, it looks like /x/denodb doesn't set up generic return types on the model functions. I was able to get your example to typecheck by only casting the return of first() to UserModel. Here's the version of signin() that I was running myself:

async function signin(user: Pick<UserModel, "username" | "password">){
    const { username, password } = user;

    const existentUser = <UserModel> await UserModel.where('username', username).first();

    if (!existentUser) throw new Error("User does not exist")

    const isPasswordCorrect = bcrypt.compareSync(password, existentUser.password);
    if (!isPasswordCorrect) throw new Error("Bad password")
}

Needing to cast to the correct model seems like something that can be improved in /x/denodb. I'll see if the project accepts a PR, but for now the above seems clean enough :)