Versions
I'm submitting a ...
[X] bug report
[ ] feature request
Actual behavior:
I have added 2 class files, Player.ts and Team.ts as per the example. In each of the references:
@HasMany(() => Player)
and
@ForeignKey(() => Team)
@Column
teamId: number;
@BelongsTo(() => Team)
team: Team;
The Team and the Player following the => on the attribute is underlines in red in VS code with the error:
Type 'typeof Team' is not assignable to type 'typeof Model'.
Construct signature return types 'Team' and 'Model<T, T2>' are incompatible.
The types of '_attributes' are incompatible between these types.
Type 'Team' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Team'.ts(2322)
model-class-getter.d.ts(2, 40): The expected type comes from the return type of this signature.
Expected behavior:
No Errors
Steps to reproduce:
Added Player.ts with the sample code provided in the readme.md, and Team.ts in the same way
Related code:
Player.ts
import { Table, Model, Column, ForeignKey, BelongsTo } from 'sequelize-typescript'
@Table
export class Player extends Model<Player> {
@Column
name: string;
@Column
num: number;
@ForeignKey(() => Team)
@Column
teamId: number;
@BelongsTo(() => Team)
team: Team;
}
Team.ts
import { Table, Model, Column, ForeignKey, HasMany } from 'sequelize-typescript'
import { Player } from './Player';
@Table
export class Team extends Model<Team> {
@Column
name: string;
@HasMany(() => Player)
players: Player[];
}
It's related to [email protected]. I have the same issue with both sequelize-typescript @1.1.0 & @2.0.0-beta.0
If I downgrade to [email protected] the issue is gone.
It must be a change in the sequelize Model type which is not compatible with the decorators typing.
Already reported in: #828, #826, #821 #813
Looks like using [email protected] solve the issue for now, which is the last version of sequelize with Model type compatible with sequelize-typescript.
It's related to [email protected]. I have the same issue with both sequelize-typescript @1.1.0 & @2.0.0-beta.0
If I downgrade to [email protected] the issue is gone.
It must be a change in the sequelize Model type which is not compatible with the decorators typing.
Already reported in: #828, #826, #821 #813
Looks like using [email protected] solve the issue for now, which is the last version of sequelize with Model type compatible with sequelize-typescript.
using [email protected] actually solves the issue, but creates another issue. It messes with the dependency injection in when using NestJS, the error is given as:
Type '(attributes: ModelAttributes<Model<any, any>>, options: InitOptions<Model<any, any>>) => void' is not assignable to type '<M extends Model<any, any> = Model<any, any>>(this: ModelCtor<M>, attributes: ModelAttributes<M>, options: InitOptions<M>) => Model<...>'.ts(2684)
Hello @Austine105, did you found any solution for this issue ? I have the same problem with nest.js
Yes I did.
I used the factory pattern to configure sequelize as a provider and inject them in each module.
// database.providers.ts
export const databaseProviders = [
{
provide: SEQUELIZE, // 'SEQUELIZE'
useFactory: async () => {
const sequelize = new Sequelize(configService.getSequelizeConfig());
sequelize.addModels([UserModel, OtherModels..]);
await sequelize.sync();
return sequelize;
},
},
];
// database.module.ts
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule { }
import DatabaseModule in your app.module file.
In a module, e.g UserModule, create the file below:
// user.provider.ts
export const UserProvider = [
{
provide: USER_REPOSITORY, // 'USER_REPOSITORY'
useValue: UserModel,
}
];
Add the provider to UserModule
// user.module.ts
...imports...
providers: [UserService, ...UserProvider], // the spread operator in UserProvider is intentional
...exports...
Finally in UserService constructor
// user.service.ts
constructor(
@Inject(USER_REPOSITORY) private readonly userRepo: typeof UserModel
) { }
// in your methods, you can now use the userRepo, e.g this.userRepo.findAndCountAll(query...)
Hello, similar to the approach outlined above, has anyone been able to build something with private declarations and a constructor model?
import {Column, DataType, Model, Table} from 'sequelize-typescript';
@Table({
timestamps: false,
freezeTableName: true,
tableName: 'author'
})
export class Author extends Model<Author> {
@Column({
type: DataType.NUMBER,
allowNull: false,
field: 'id',
primaryKey: true,
})
private _id: Number;
@Column({
type: DataType.STRING,
allowNull: false,
field: 'name',
})
private _name: string;
@Column({
type: DataType.STRING,
allowNull: false,
field: 'code',
})
private _code: string;
@Column({
type: DataType.STRING,
allowNull: false,
field: 'created_at',
})
private _createdAt: Date;
constructor(id: Number, name: string, code: string) {
super();
this._id = id;
this._name = name;
this._code = code;
this._createdAt = new Date();
}
public updateAuthor(name: string, code: string) {
this._name = name;
this._code = code;
}
get id(): Number {
return this._id;
}
get name(): string {
return this._name;
}
get code(): string {
return this._code;
}
}
I would love to build a rich DDD style model, but I keep getting this error:
The 'this' context of type 'typeof Author' is not assignable to method's 'this' of type '(new () => Author) & typeof Model'.
Type 'typeof Author' is not assignable to type 'new () => Author'.
Would love to see if someone has tried similar approaches.
Same here, @RobinBuschmann any idea what's going on?
I have the same isssue with [email protected] and [email protected]
I have the same problem with
"sequelize": "^6.3.5",
"sequelize-typescript": "^2.0.0-beta.0"
and
"sequelize": "^6.1.1",
"sequelize-typescript": "^2.0.0-beta.0"
resolved with downgrade to
"sequelize": "^5.22.0",
"sequelize-typescript": "^1.1.0"
I've created a simple reproduction in TypeScript Playground (Here). I'm not that good at TypeScript but when it's in it's simplest form it's easier to play around with it until the error disappears and we can PR/patch it. After playing with it a little I asked in https://gitter.im/Microsoft/TypeScript and a guy named webstrand helped me understand what was wrong:
The issue is actually in your definition of Model.
TheTCreationAttributes extends {} = TModelAttributescauses problems becauseextends {}doesn't require aname: stringproperty
Or, in other words: You cannot safely cast typeof Project into typeof Model because you construct Project like
new Project({ name: "foo" /* this property is required */ })
Whereas, if you cast it to typeof Model, you would be able to unsafely callnew (Project as typeof Model)({ }), which is an error.
You can fix by threading more generic information through. playground example
{ prototype: Model<{}, {}>; new (v?: {}, options?: any) => Model<{}, {}> }is roughly equivalent totypeof Model
I don't need prototype, so I just specify the constructor type in ModelClassGetter
Also, beware that typescript is structurally typed.
interface Foo { name: string }is exactly the same, and indistinguishable from, thisinterface Bar { name: string }
which is why I added attr to OriginModel, otherwiseOriginModel<{ name: string }>is the same asOriginModel<{ id: 5000 }>as far as TypeScript is concerned
It doesn't care about or compare generic arguments unless they're used in some property or method defined on the class.
Here's the Playground example that fixes it, which also seems to work in other places like Includable which also has the same problem. It's a bit verbose but maybe it's the necessary evil to achieve what we want.
TL;DR:
export type TypeOfModel<TCreationAttributes, TModelAttributes> = new (values?: TCreationAttributes, options?: any) => Model<TModelAttributes, TCreationAttributes>;
export type Includeable<TCreationAttributes, TModelAttributes> = TypeOfModel<TModelAttributes, TCreationAttributes> | Association | IncludeOptions | { all: true, nested?: true } | string;
export declare type ModelClassGetter<TCreationAttributes, TModelAttributes> = (returns?: void) => TypeOfModel<TCreationAttributes, TModelAttributes>;
export declare function HasOne<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, foreignKey?: string): Function;
export declare function HasOne<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, options?: HasOneOptions): Function;
etc
I have the same problem with
"sequelize": "^6.3.5", "sequelize-typescript": "^2.0.0-beta.0"and
"sequelize": "^6.1.1", "sequelize-typescript": "^2.0.0-beta.0"resolved with downgrade to
"sequelize": "^5.22.0", "sequelize-typescript": "^1.1.0"
Thanks your proposal can solve the issue
You can use sequelize^6.3.5 + sequelize-typescript^2.0.0-beta.1, you only need to remove argument from Model<...>:
-export class Team extends Model<Team> {
+export class Team extends Model {
Thanks to @lukashroch for updated docs: https://github.com/lukashroch/sequelize-typescript/tree/sequelize6-docs#v6-model-definition-less-strict
Hi guys, I provide a workaround to allow strict model attribute type-checks. Works for:
import {
AutoIncrement,
Column,
HasMany,
Model,
ModelCtor,
PrimaryKey,
Sequelize,
Table,
} from 'sequelize-typescript';
type ModelAttributes<T> = Omit<T, keyof Model>;
type CreationAttributes<T> = {
[key in keyof ModelAttributes<T>]?: ModelAttributes<T>[key];
};
// 1. Created a strict typed model class
export class TypedModel<T> extends Model<ModelAttributes<T>, CreationAttributes<T>> {
// provides a less strict type Model
static get Model(): ModelCtor {
return this as any;
}
}
// 2. Extends TypedModel instead of Model
@Table
class User extends TypedModel<User> {
@AutoIncrement
@PrimaryKey
@Column
userId: number;
@Column
userName: string;
}
@Table
class Team extends TypedModel<Team> {
@AutoIncrement
@PrimaryKey
@Column
teamId: number;
// 3. Use less strict type Model to make type check successfully
@HasMany(() => User.Model)
members: User[];
}
const sequelize = new Sequelize({
// ... other initialize options
// 4. Just use less strict type Model wherever you needs
models: [User.Model, Team.Model],
});
User.findAll({
// 5. now you got a type-safe where!
where: {
userName: 'techirdliu',
},
});
@KapitanOczywisty solution also works with:
So i think we can work free with the latest versions
I'm not sure if the above fixes the problem with Includeables:
PaymentCard.findOne({
transaction,
include: [BillingAddress],
where: { active: true },
}),
Gives:
No overload matches this call.
Overload 1 of 2, '(this: ModelStatic<PaymentCard>, options: NonNullFindOptions<PaymentCardAttrs>): Promise<PaymentCard>', gave the following error.
Type 'typeof BillingAddress' is not assignable to type 'Includeable'.
Type 'typeof BillingAddress' is not assignable to type 'typeof Model'.
Types of construct signatures are incompatible.
Type 'new (values?: BillingAddressCreateAttrs, options?: BuildOptions) => BillingAddress' is not assignable to type 'new <TModelAttributes extends {} = any, TCreationAttributes extends {} = TModelAttributes>(values?: TCreationAttributes, options?: BuildOptions) => Model<TModelAttributes, TCreationAttributes>'.
Types of parameters 'values' and 'values' are incompatible.
Type 'TCreationAttributes' is not assignable to type 'BillingAddressCreateAttrs'.
Type '{}' is missing the following properties from type 'BillingAddressCreateAttrs': paymentCardId, name, lastName, address1, and 6 more.
Overload 2 of 2, '(this: ModelStatic<PaymentCard>, options?: FindOptions<PaymentCardAttrs>): Promise<PaymentCard>', gave the following error.
Type 'typeof BillingAddress' is not assignable to type 'Includeable'.
Type 'typeof BillingAddress' is not assignable to type 'typeof Model'.ts(2769)
And I think it's the initial problem I mentioned in my original PR. That Sequelize has it's own Model types that we'd need to override somehow.
It works if you remove the Attrs and CreateAttr types:
@Table
export class PaymentCard extends Model {
@HasOne(() => BillingAddress)
public billingAddress: BillingAddress;
}
@Table
export class BillingAddress extends Model {...}
There is a PR in the sequelize repo to fix this :
Most helpful comment
You can use
sequelize^6.3.5+sequelize-typescript^2.0.0-beta.1, you only need to remove argument fromModel<...>:Thanks to @lukashroch for updated docs: https://github.com/lukashroch/sequelize-typescript/tree/sequelize6-docs#v6-model-definition-less-strict