# PartialEq
The `PartialEq` macro generates an `equals()` method for field-by-field
structural equality comparison. This is analogous to Rust's `PartialEq` trait,
enabling value-based equality semantics instead of reference equality.
## Generated Output
| Type | Generated Code | Description |
|------|----------------|-------------|
| Class | `classNameEquals(a, b)` + `static equals(a, b)` | Standalone function + static wrapper method |
| Enum | `enumNameEquals(a: EnumName, b: EnumName): boolean` | Standalone function using strict equality |
| Interface | `interfaceNameEquals(a: InterfaceName, b: InterfaceName): boolean` | Standalone function comparing fields |
| Type Alias | `typeNameEquals(a: TypeName, b: TypeName): boolean` | Standalone function with type-appropriate comparison |
## Comparison Strategy
The generated equality check:
1. **Identity check**: `a === b` returns true immediately
2. **Field comparison**: Compares each non-skipped field
## Type-Specific Comparisons
| Type | Comparison Method |
|------|-------------------|
| Primitives | Strict equality (`===`) |
| Arrays | Length + element-by-element (recursive) |
| `Date` | `getTime()` comparison |
| `Map` | Size + entry-by-entry comparison |
| `Set` | Size + membership check |
| Objects | Calls `equals()` if available, else `===` |
## Field-Level Options
The `@partialEq` decorator supports:
- `skip` - Exclude the field from equality comparison
## Example
```typescript before
/** @derive(PartialEq, Hash) */
class User {
id: number;
name: string;
/** @partialEq({ skip: true }) @hash({ skip: true }) */
cachedScore: number;
}
```
```typescript after
class User {
id: number;
name: string;
cachedScore: number;
static equals(a: User, b: User): boolean {
return userEquals(a, b);
}
static hashCode(value: User): number {
return userHashCode(value);
}
}
export function userEquals(a: User, b: User): boolean {
if (a === b) return true;
return a.id === b.id && a.name === b.name;
}
export function userHashCode(value: User): number {
let hash = 17;
hash =
(hash * 31 +
(Number.isInteger(value.id)
? value.id | 0
: value.id
.toString()
.split('')
.reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) |
0;
hash =
(hash * 31 +
(value.name ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)) |
0;
return hash;
}
```
## Equality Contract
When implementing `PartialEq`, consider also implementing `Hash`:
- **Reflexivity**: `a.equals(a)` is always true
- **Symmetry**: `a.equals(b)` implies `b.equals(a)`
- **Hash consistency**: Equal objects must have equal hash codes
To maintain the hash contract, skip the same fields in both `PartialEq` and `Hash`:
```typescript before
/** @derive(PartialEq, Hash) */
class User {
id: number;
name: string;
/** @partialEq({ skip: true }) @hash({ skip: true }) */
cachedScore: number;
}
```
```typescript after
class User {
id: number;
name: string;
cachedScore: number;
static equals(a: User, b: User): boolean {
return userEquals(a, b);
}
static hashCode(value: User): number {
return userHashCode(value);
}
}
export function userEquals(a: User, b: User): boolean {
if (a === b) return true;
return a.id === b.id && a.name === b.name;
}
export function userHashCode(value: User): number {
let hash = 17;
hash =
(hash * 31 +
(Number.isInteger(value.id)
? value.id | 0
: value.id
.toString()
.split('')
.reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) |
0;
hash =
(hash * 31 +
(value.name ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)) |
0;
return hash;
}
```