Is there an elegant way to have value objects in typescript?

Maxim Pyshko

The problem

For validation purposes I want to have some value objects in the core of my system. These value objects could be classes:

class UserName {
  readonly value: string;

  constructor(value: string) {
    this.value = value.trim();
    if (this.value === '') {
      throw new Error('Empty value');
    }
  }
}

// and use it

userService.updateName({id: 1, name: new UserName('Maxim')});

But it does not work well because everyone can call the service without instantiating the class and therefore without validation:

userService.updateName({id: 1, name: {value: INVALID_NAME});

My solution

I have an interface and utility function to create object that is not convenient to create manually:

interface ValueObject<T extends string, U> {
  readonly type: T;
  readonly value: U;
  readonly DO_NOT_CREATE_MANUALLY: 'DO NOT CREATE THIS MANUALLY!',
}

function ValueObject<T extends string, U>(type: T, value: U): ValueObject<T, U> {
  return {
    type,
    value,
    DO_NOT_CREATE_MANUALLY: 'DO NOT CREATE THIS MANUALLY!',
  };
}

And I have an interface for value object and utility function:

interface UserName extends ValueObject<'UserName', string> {}

function UserName(value: string): UserName {
  value = value.trim();
  if (value === '') {
    throw new Error('Empty value');
  }

  return ValueObject('UserName', value);
}

I use it like that:

userService.updateName({id: 1, name: UserName('Maxim')});

It works well. No one will write:

userService.updateName({
  id: 1,
  name: {
    value: INVALI_VALUE,
    type: 'UserName',
    DO_NOT_CREATE_MANUALLY: 'DO NOT CREATE THIS MANUALLY!',
  },
});

The question

Is there a more elegant way to have value objects in typescript without DO_NOT_CREATE_MANUALLY?

Kryten

To flesh out the comment from @titian a little more...

Consider the following class:

class UserName {
    public readonly value: string;
    constructor(value: string) {
        if (value === '') {
            throw new Error('Empty value');
        }
        this.value = value;
    }
}

According to the Typescript type compatibility documentation, any object with a value: string property will be compatible with an instance of this class. This is what you are trying to avoid - you want to ensure that any UserName-like object passed to userService.updateName is, in fact, a UserName object.

The solution is to add a private property to the class so that any UserName-like object must also have that private property:

class UserName {
    public readonly value: string;
    private readonly is_nominal: boolean = true;
    constructor(value: string) {
        if (value === '') {
            throw new Error('Empty value');
        }
        this.value = value;
    }
}

Now, if someone does the following:

userService.updateName({ id: 1, value: { value: 'James T Kirk' } })`

Typescript will complain, since { value: 'James T Kirk' } is missing the private is_nominal property.

There is, however, a somewhat simpler (and IMHO cleaner) way to add a private field: make the value private and add an accessor.

class UserName {
    private readonly _value: string;
    constructor(value: string) {
        if (value === '') {
            throw new Error('Empty value');
        }
        this._value = value;
    }

    get value() { return this._value; }
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Initializing many objects in an elegant way

From Dev

What is an elegant Ruby way to have a condition of 'nil'?

From Dev

Elegant way of fetching multiple objects in custom order

From Java

Elegant way to add a value to a class variable in java

From Dev

Elegant way to check if either value in a Tuple is nil

From Dev

Elegant way to connect an enum value to a method group

From Dev

elegant way of extracting option value when need it

From Dev

Elegant way to html escape string that may already have been escaped

From Dev

Elegant way to refactor multiple functions that have same signatures?

From Dev

Seeking an elegant way for inherited classes to have a "StateName" property

From Dev

What is the more elegant way to serialize my own objects with arrays in Swift

From Javascript

What is the most elegant way to insert objects between array elements?

From Dev

In JavaScript there an elegant way to merge two Objects and sum any common properties?

From Java

Elegant way of encapsulating behaviour that depends on the combination of involved objects?

From Dev

elegant way to extract a certain member from a list of objects

From Dev

Elegant way to select nested objects with the associated key based on a specific criteria

From Dev

The most elegant way to collect nonEmpty object fields from the list of objects

From Dev

Managing pointers to destroyed objects in a game engine in an elegant way

From Dev

Elegant way to convert json to list of objects in Python 3

From Dev

Elegant way to access objects created in qApplication main function

From Dev

TypeScript: Elegant way to write single line helper functions

From Dev

Elegant way of assigning value to variable according to the value of another variable

From Dev

Elegant way to construct a pipeline based on return value and not exit code?

From Dev

A more elegant way of deleting the last value of multiple lists?

From Dev

What is the most elegant way to extract an value from a database

From Dev

What is the most elegant way to create a dictionary with the value as lists?

From Dev

Is there an elegant way to initialize and return value of nullable field using Optional

From Python

Most elegant way to store (persist) the value of a single (numeric) variable

From Java

Most elegant way to assign multiple variables to the same value?

Related Related

  1. 1

    Initializing many objects in an elegant way

  2. 2

    What is an elegant Ruby way to have a condition of 'nil'?

  3. 3

    Elegant way of fetching multiple objects in custom order

  4. 4

    Elegant way to add a value to a class variable in java

  5. 5

    Elegant way to check if either value in a Tuple is nil

  6. 6

    Elegant way to connect an enum value to a method group

  7. 7

    elegant way of extracting option value when need it

  8. 8

    Elegant way to html escape string that may already have been escaped

  9. 9

    Elegant way to refactor multiple functions that have same signatures?

  10. 10

    Seeking an elegant way for inherited classes to have a "StateName" property

  11. 11

    What is the more elegant way to serialize my own objects with arrays in Swift

  12. 12

    What is the most elegant way to insert objects between array elements?

  13. 13

    In JavaScript there an elegant way to merge two Objects and sum any common properties?

  14. 14

    Elegant way of encapsulating behaviour that depends on the combination of involved objects?

  15. 15

    elegant way to extract a certain member from a list of objects

  16. 16

    Elegant way to select nested objects with the associated key based on a specific criteria

  17. 17

    The most elegant way to collect nonEmpty object fields from the list of objects

  18. 18

    Managing pointers to destroyed objects in a game engine in an elegant way

  19. 19

    Elegant way to convert json to list of objects in Python 3

  20. 20

    Elegant way to access objects created in qApplication main function

  21. 21

    TypeScript: Elegant way to write single line helper functions

  22. 22

    Elegant way of assigning value to variable according to the value of another variable

  23. 23

    Elegant way to construct a pipeline based on return value and not exit code?

  24. 24

    A more elegant way of deleting the last value of multiple lists?

  25. 25

    What is the most elegant way to extract an value from a database

  26. 26

    What is the most elegant way to create a dictionary with the value as lists?

  27. 27

    Is there an elegant way to initialize and return value of nullable field using Optional

  28. 28

    Most elegant way to store (persist) the value of a single (numeric) variable

  29. 29

    Most elegant way to assign multiple variables to the same value?

HotTag

Archive