Typescript

There are different ways to write the classes and having typescript types. I want to show you here the different approaches.

Requirements

Since version 2.0.0 the decorators are standard ECMAScript decorators. They require:

  • TypeScript >= 5.2 (5.2 added decorator metadata support)
  • No experimentalDecorators flag — remove it from your tsconfig.json if it is still set. The legacy experimentalDecorators mode is not supported anymore.
{
  "compilerOptions": {
    // remove these two lines if you still have them:
    // "experimentalDecorators": true,
    // "useDefineForClassFields": false,
  }
}

Declare the fields with the definite assignment assertion (!). The decorators register the field and keep the hydrated value, so no initializer is needed.

import { Model } from 'pinia-orm'
import { Attr, Cast, Uid } from 'pinia-orm/decorators'
import { ArrayCast } from 'pinia-orm/casts'
class User extends Model {
  static entity = 'users'
  @Uid() id!: string
  @Cast(() => ArrayCast) @Attr('{}') meta!: Record<string, any>
}
Do not combine decorators with the declare keyword anymore — standard decorators cannot be applied to declare fields and TypeScript will report an error. Simply replace @Attr('') declare name: string with @Attr('') name!: string.

Without decorators (used way from vuex-orm)

When defining the schema with the static fields() method, type the properties with declare so no actual class fields are emitted (class fields would overwrite the hydrated values):

import { Model } from 'pinia-orm'
import { ArrayCast } from 'pinia-orm/casts'
class User extends Model {
  static entity = 'users'
  static fields() {
    return {
      id: this.uid(),
      meta: this.attr('{}')
    }
  }
  static casts() {
    return {
      meta: ArrayCast,
    }
  }
  declare id: number
  declare meta: Record<string, any>
}

Overriding fields in subclasses

When a subclass overrides a decorated field of its base class (e.g. the discriminator field in Single Table Inheritance), TypeScript requires an initializer instead of the ! assertion:

class Dog extends Animal {
  static entity = 'dogs'
  static baseEntity = 'animals'
  @Attr('dog') type = 'dog'
}

The initializer value itself is never used — the decorator keeps the hydrated value — but it satisfies TypeScript's override check and documents the default.