A TypeScript library that implements Rust-like traits with compile-time type checking and runtime verification.
- 🔒 Type Safety - Type-safe trait definitions and implementations
- 🎯 Generics - Support for generic traits and implementations
- 🔄 Methods - Instance and static method implementations
- 🔗 Composition - Trait-to-trait implementations
- 💾 Memory - Memory-efficient using WeakMap for GC
npm install @rustable/trait
# or
yarn add @rustable/trait
# or
pnpm add @rustable/trait
import { Trait, macroTrait } from '@rustable/trait';
// Define a trait
class DisplayTrait extends Trait {
display(): string {
return 'default';
}
}
// Create a trait decorator
const Display = macroTrait(DisplayTrait);
There are several ways to implement a trait:
- Using the
@derive
decorator with default implementation:
@derive([Display])
class Point {
constructor(
public x: number,
public y: number,
) {}
}
// The display method will use the default implementation from DisplayTrait
- Using
implFor
method with custom implementation:
class Point {
constructor(
public x: number,
public y: number,
) {}
}
DisplayTrait.implFor(Point, {
display() {
return `(${this.x}, ${this.y})`;
},
});
Once a trait is implemented, you can use it in several ways:
const point = new Point(1, 2);
// Method 1: Using wrap
const display = DisplayTrait.wrap(point);
console.log(display.display()); // "(1, 2)"
// Method 2: Checking implementation
if (DisplayTrait.isImplFor(point)) {
const display = DisplayTrait.wrap(point);
console.log(display.display());
}
Traits can also include static methods:
class FromStrTrait extends Trait {
static fromStr(s: string): any {
throw new Error('Not implemented');
}
}
const FromStr = macroTrait(FromStrTrait);
class Point {
constructor(
public x: number,
public y: number,
) {}
}
// Implement static methods using implFor
FromStr.implFor(Point, {
static: {
fromStr(s: string): Point {
const [x, y] = s.split(',').map(Number);
return new Point(x, y);
},
},
});
// Use static trait methods
const point = FromStrTrait.staticWrap(Point).fromStr('1,2');
Creates a trait decorator for implementing traits at compile time.
const Display = macroTrait(DisplayTrait);
const FromStr = macroTrait(FromStrTrait);
@derive([Display, FromStr])
class Point {
constructor(
public x: number,
public y: number,
) {}
}
// Implement custom behavior using implFor
DisplayTrait.implFor(Point, {
display() {
return `(${this.x}, ${this.y})`;
},
});
FromStrTrait.implFor(Point, {
static: {
fromStr(s: string): Point {
const [x, y] = s.split(',').map(Number);
return new Point(x, y);
},
},
});
The Trait
class provides several static methods for trait operations:
Checks if a value implements the trait.
if (DisplayTrait.isImplFor(point)) {
// point implements DisplayTrait
const display = DisplayTrait.wrap(point);
console.log(display.display());
}
Validates that a value implements the trait. Throws if validation fails.
// Throws if point doesn't implement DisplayTrait
DisplayTrait.validFor(point);
Wraps a value as a trait instance. Supports both instance and constructor wrapping.
// Wrap instance
const point = new Point(1, 2);
const display = DisplayTrait.wrap(point);
console.log(display.display());
// Wrap constructor
const PointDisplay = DisplayTrait.wrap(Point);
const newPoint = new PointDisplay(3, 4);
Wraps a class to access static trait methods.
// Wrap Point's static methods
const PointFromStr = FromStrTrait.staticWrap(Point);
const point = PointFromStr.fromStr('1,2');
Implements a trait for a target class.
DisplayTrait.implFor(Point, {
display() {
return `(${this.x}, ${this.y})`;
},
});
Similar to implFor
, but doesn't throw if the trait is already implemented.
DisplayTrait.tryImplFor(Point, {
display() {
return `(${this.x}, ${this.y})`;
},
});
MIT © illuxiza