Skip to main content
Version: v7 - alpha

Typing your Model with TypeScript

Automated Attribute Typing

Model methods are strictly typed based on your attributes, but require you to define you to write a bit of configuration first. Model accept two generic types that you can use to let it know what its Attributes & Creation Attributes are like:

You can use InferAttributes, and InferCreationAttributes to define them. They will extract Attribute typings directly from the Model:

import { Attribute, PrimaryKey, AutoIncrement, NotNull } from '@sequelize/core/decorators-legacy';
import { Model, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';

// order of InferAttributes & InferCreationAttributes is important.
export class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
@AutoIncrement
// 'CreationOptional' is a special type that marks the attribute as optional
// when creating an instance of the model (such as using Model.create()).
declare id: CreationOptional<number>;

@Attribute(DataTypes.STRING)
@NotNull
declare name: string;
}

// TypeScript will now be able to catch errors such as this typo:
await User.create({ nAme: 'john' });

Important things to know about how InferAttributes & InferCreationAttributes work, they will select all declared properties of the class, except for:

  • Static fields and methods.
  • Methods (anything whose type is a function).
  • Those whose type uses the branded type NonAttribute.
  • Those excluded by using InferAttributes like this: InferAttributes<User, { omit: 'properties' | 'to' | 'omit' }>.
  • Those declared by the Model superclass (but not intermediary classes!). If one of your attributes shares the same name as one of the properties of Model, change its name. Doing this is likely to cause issues anyway.
  • Getter & setters are not automatically excluded. Set their return / parameter type to NonAttribute, or add them to omit to exclude them.

InferCreationAttributes works the same way as InferAttributes with one exception; properties typed using the CreationOptional type will be marked as optional. Note that attributes that accept null, or undefined do not need to use CreationOptional:

export class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.STRING)
@NotNull
declare firstName: string;

// there is no need to use CreationOptional on firstName because nullable attributes
// are always optional when creating an instance of the model.
@Attribute(DataTypes.STRING)
declare lastName: string | null;
}

// last name omitted, but this is still valid!
await User.create({ firstName: 'Zoé' });

You only need to use CreationOptional & NonAttribute on class instance fields or getters.

Example of a minimal TypeScript project with strict type-checking for attributes:

Manual Attribute Typing

InferAttributes & InferCreationAttributes don't cover all use cases yet. If these type don't work for you, you can also define your attributes manually, although it is much more verbose:

import { Model } from '@sequelize/core';
import type { PartialBy } from '@sequelize/utils';

type UserAttributes = {
id: number;
name: string;
};

// we're telling the Model that 'id' is optional
// when creating an instance of the model (such as using Model.create()).
type UserCreationAttributes = PartialBy<UserAttributes, 'id'>;

class User extends Model<UserAttributes, UserCreationAttributes> {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
@AutoIncrement
declare id: number;

@Attribute(DataTypes.STRING)
@NotNull
declare string: number;
}