Effect CommunityEC
Effect Community2y ago
20 replies
mavix

Creating a Value Object Base Class with `Data.Class` in TypeScript

Hello everyone!

I am trying to model a Value Object using Data.Class, to take advantage of the equality and hashing qualities. I would not like to extend each of my classes that model my value objects from the Data.Class class, but rather, extend from an abstract class called ValueObject which in turn, extends from Data.Class, thus being more explicit in my intent. The problem is that I lose any kind of auto-completion when I instantiate an object from my value object, since I cannot pass generics to Data.Class.

import { Data } from 'effect';

type ValueObjectProps = Record<string, any>;

// Value Object base class
export abstract class ValueObject<
  T extends ValueObjectProps,
> extends Data.Class<ValueObjectProps> {
  protected constructor(protected props: T) {
    super(props);
  }
}

interface EmailProps {
  value: string;
  provider: string;
}

// Value Object
export class Email extends ValueObject<EmailProps> {
  constructor(value: string, provider: string) {
    super({ value, provider });
  }
}

const myEmail = new Email('foo@gmail.com', 'gmail');
myEmail.  // No auto-completion


I cannot do neither Data.Class<T> nor Data.Class<{ readonly [P in keyof T]: T[P] }>

The only thing I could come up with is to create getters for each attribute:

import { Data } from 'effect';

type ValueObjectProps = Record<string, any>;

// Value Object base class
export abstract class ValueObject<
  T extends ValueObjectProps,
> extends Data.Class<ValueObjectProps> {
  protected constructor(protected props: T) {
    super(props);
  }

  protected get<K extends keyof T>(key: K): T[K] {
    return this.props[key];
  }
}

// Value Object
export class Email extends ValueObject<EmailProps> {
  constructor(value: string, provider: string) {
    super({ value, provider });
  }

  get value(): string {
    return this.get('value');
  }

  get provider(): string {
    return this.get('provider');
  }
}


Is there a better way?
Was this page helpful?