Value Object in Typescript, Python, Java, and Rust

Leo Wu
3 min readMay 24, 2020

A value object is a small object that represents a simple entity whose equality is not based on identity: i.e. two value objects are equal when they have the same value, not necessarily being the same object.

wiki

When you’re coding something, no matter it’s your own small side-project or the day-to-day complex corporate work, you’re working around some kind of “domain”. And there’re many domain entities that you want to put them into your code and probably also database tables for persistence.

For example, if you work at Uber, you may have an entity called Passenger, including a passenger’s data like name, telephone…etc. And your job is to write some beautiful code to add the logic to operate on the entity Passenger. Say, to implement CRUD operations on it.

And these days type-safety is coming back. Making sure an entity structure always looks like what it should be is important (e.g no typos, no more/fewer properties) in terms of both development experience and the soundness of the business logic. Let’s say in Javascript, it’s super easy to write const passenger = { name: 'foo', telephone: '+4912345678' }, but without type safety, when you see, for example, function something(passenger) {...} in the codebase, you might think “What the hell is the passenger and why am I supposed to know its definition??!!”.

So we need type-safe value object to encapsulate the definition of the entities and allow us to work on them safely and conveniently.

Below I’ll list the most common ways to implement value object (afaik) in 4 languages using the example Point

*I use the first three in my work and I’m learning Rust :)

Typescript

interface Point {
x: number
y: number
}
const point: Point = { x: 1.2, y: 3.4 };
const point: Readonly<Point> = { x: 1.2, y: 3.4 }; // Immutable
console.log(point.x)

Nothing interesting, just type-safe, and the ability to make an object truly immutable.

Python

from dataclasses import dataclass@dataclass
class Point:
x: float
y: float
@dataclass(frozen=True) # Immutable
class Point:
x: float
y: float
point = Point(x=1.2, y=3.4)
print(point.x)

Playground

dataclass is the decorator to help you create a value object in a concise way with all the common functionalities which a value object needs (e.g __eq__ for comparing two value objects using ‘==’, __hash__ for using the object as a dict key if it’s immutable) provided. It’s similar to the AutoValue library in Java (below) to save you from writing the tedious boilerplates.

Java

import com.google.auto.value.AutoValue;

@AutoValue
abstract class Point {
abstract float x();
abstract float y();
static Point create(float x, float y) {
return new AutoValue_Point(x, y);
}
}
// Usage
var point = Point.create(1.2, 3.4)

Java is notoriously verbose. If you use the primitive class to implement a value class by yourself, it may be the most boring thing you’ve ever done in your life, because you’ll need to define a lot of things to make it usable as a value object.
AutoValue came to rescue by Google because they use Java a lot and they know the pain. Unlike Python’s dataclass, an AutoValue class can only be immutable. You can update the value by providing withYourFieldName methods, but it’ll return a new object so the original one stays untouched.
However, even with AutoValue, it’s still painful to create a value object without keyword arguments limited by Java :(

Rust

#[derive(Hash, Eq, PartialEq, Debug)]
struct Point {
x: i32,
y: i32
}
fn main() {
// Usage
let point = Point {
x: 1,
y: 3
};
println!("{}", point.x);
}

Playground

Looks pretty similar to Typescript isn’t it? 👀 And the same concept as the decorator in Python and Java, you can make a struct as a fully-fledged value object by using the derive attribute to enhance a struct with additional trait implementations, then you can use your value object as a HashMap key for example.

--

--