adopted-ember-addons / validated-changeset

Buffering changes to form data
MIT License
36 stars 27 forks source link

Changeset types should be generic #173

Open vlascik opened 2 years ago

vlascik commented 2 years ago

It would help the usability of changesets in Typescript if types for changesets were generic, e.g. BufferedChangeset<UserModel>, that way we would have the proper type safety and wouldn't have to rely on type casting constantly.

snewcomer commented 2 years ago

@vlascik 👋 I might need a little help in understanding a few points. So forgive me for a sec. The return value of stamping out a changeset is a Proxy. Do you see any problems with that?

vlascik commented 2 years ago

Right, I imagine typing proxies could be an issue (never tried it), but seems that people have come up with some workarounds (e.g. here https://stackoverflow.com/questions/59390026/how-to-use-es6-proxy-in-typescript). Can you maybe try to flesh it out, and if you have issues maybe we can summon the gods of typescript at #topic-typescript? :D

bakerac4 commented 2 years ago
  1. I was just coming here to add an issue like this, so Im glad Im not the only one.
  2. @vlascik I use this in our validation addon built on ember-changeset/ember-changeset validations and it works fairly well. Obviously doesn't provide even close to property type safety but for most use cases thats all we need it for.

EDIT: Im actually working on improving this as we speak. Here is the current version.

export type ModelAttributes<T extends Model> = Pick<T, Exclude<keyof T, keyof Model>>;
export type ModelProperties<T extends Model> = Readonly<Pick<T, Exclude<keyof T, keyof ModelAttributes<T>>>>;
export type ChangesetAttributes<T> = T extends Model ? ModelAttributes<T> & ModelProperties<T> : T;
export type GenericChangeset<T> = BufferedChangeset & ChangesetAttributes<T> & { data: T };
miguelcobain commented 2 months ago

@bakerac4 those types were a great help! Thank you.

For reference, this is what I ended up with in my types/index.d.ts:

// fix changeset types
// taken from https://github.com/validated-changeset/validated-changeset/issues/173
import type Model from '@ember-data/model';
import type { Changeset } from 'ember-changeset';

export type ModelAttributes<T extends Model> = Pick<T, Exclude<keyof T, keyof Model>>;
export type ModelProperties<T extends Model> = Readonly<Pick<T, Exclude<keyof T, keyof ModelAttributes<T>>>>;
export type ChangesetAttributes<T> = T extends Model ? ModelAttributes<T> & ModelProperties<T> : T;
export type BufferedChangeset = ReturnType<typeof Changeset>;
export type GenericChangeset<T> = BufferedChangeset & ChangesetAttributes<T> & { data: T };

and then using it in a component

import { Changeset } from 'ember-changeset';

import type MyModel from 'my-app/models/my-model';
import type { GenericChangeset } from 'index';

class EditModel extends Component<PropertyGeneralSignature> {
  changeset = Changeset(this.args.myModel) as GenericChangeset<MyModel>;

would be great if ember-changeset could have this return type instead.

What do you think @snewcomer ?