Generics
Chuks supports generics across classes, data types, interfaces, functions, and methods. Generics let you write reusable, type-safe code that works with any type while preserving type annotations for documentation and tooling.
Generic Classes
Section titled “Generic Classes”Declare type parameters in angle brackets after the class name:
class Box<T> { var value: T
constructor(v: T) { this.value = v }
public getValue(): T { return this.value }
public setValue(v: T): void { this.value = v }}Instantiate with explicit type arguments:
var intBox = new Box<int>(42)println(intBox.getValue()) // 42
var strBox = new Box<string>("hello")println(strBox.getValue()) // hello
var floatBox = new Box<float>(3.14)println(floatBox.getValue()) // 3.14Multiple Type Parameters
Section titled “Multiple Type Parameters”Classes can have any number of type parameters:
class Pair<A, B> { var first: A var second: B
constructor(a: A, b: B) { this.first = a this.second = b }
public getFirst(): A { return this.first }
public getSecond(): B { return this.second }}
var p = new Pair<string, int>("age", 25)println(p.getFirst()) // ageprintln(p.getSecond()) // 25Generic Data Types
Section titled “Generic Data Types”The dataType keyword also supports generic parameters:
dataType Point<T> { x: T; y: T;}
dataType Pair<K, V> { first: K; second: V;}Use with explicit type annotations:
var p1: Point<int> = { "x": 1, "y": 2 }println(p1.x) // 1
var p2: Point<string> = { "x": "hello", "y": "world" }println(p2.y) // world
var kv: Pair<string, int> = { "first": "age", "second": 30 }println(kv.second) // 30Generic Interfaces
Section titled “Generic Interfaces”Interfaces can declare type parameters for generic contracts:
interface Repository<T> { findById(id: int): T? save(item: T): void delete(id: int): bool}
interface Mapper<S, D> { map(source: S): D}Classes implement generic interfaces with concrete types:
class UserRepository implements Repository<User> { public findById(id: int): User? { // look up user... return null }
public save(item: User): void { // persist user... }
public delete(id: int): bool { // delete user... return true }}Generic Functions
Section titled “Generic Functions”Top-level functions can declare their own type parameters:
function identity<T>(value: T): T { return value}
function swap<A, B>(pair: Pair<A, B>): Pair<B, A> { return new Pair<B, A>(pair.second, pair.first)}
function toArray<T>(item: T): []T { return [item]}Generic Methods
Section titled “Generic Methods”Class methods can have their own type parameters, independent of the class’s type parameters:
class Registry { var data: any
constructor() { this.data = {} }
public define<T>(name: string, value: any): any { this.data[name] = value return this.data[name] }
public get<T>(name: string): any { return this.data[name] }}Call generic methods with explicit type arguments:
var reg = new Registry()reg.define<string>("greeting", "hello")var val: any = reg.get<string>("greeting")println(val) // hello
reg.define<int>("count", 42)println(reg.get<int>("count")) // 42This pattern is used throughout the standard library. For example, db.define<T>() accepts a type argument to associate a schema with a data type:
const UserSchema: Schema = db.define<User>("users", (schema: SchemaBuilder) => { schema.pk("id").auto() schema.string("name").notNull()})Generic Extends
Section titled “Generic Extends”A class can extend a generic parent class with a concrete type argument:
class Container<T> { var items: []any
constructor() { this.items = [] }
public add(item: T): void { this.items.push(item) }
public getAll(): []any { return this.items }}
class StringContainer extends Container<string> { constructor() { super() }
public first(): any { if (length(this.items) > 0) { return this.items[0] } return null }}Usage:
var sc = new StringContainer()sc.add("hello")sc.add("world")println(sc.first()) // helloprintln(length(sc.getAll())) // 2This is the foundation of the Repository pattern in the standard library:
// Repository<T> is a generic base class in std/db/repositoryclass UserRepo extends Repository<User> { constructor() { super(UserSchema) }
async findByEmail(email: string): Task<any> { return await this.where("email", email).first() }}Built-in Generic Types
Section titled “Built-in Generic Types”Chuks has several built-in types that use generic syntax:
Task<T>
Section titled “Task<T>”The return type for async functions. T is the resolved value type.
async function fetchData(): Task<string> { return "data"}
var result: string = await fetchData()[]T (Array Type)
Section titled “[]T (Array Type)”Typed arrays use bracket syntax:
var nums: []int = [1, 2, 3]var names: []string = ["Alice", "Bob"]var nested: [][]int = [[1, 2], [3, 4]]map[K]V (Map Type)
Section titled “map[K]V (Map Type)”Typed maps:
var scores: map[string]int = { "Alice": 95, "Bob": 87 }Nested Generics
Section titled “Nested Generics”Chuks correctly parses nested generic types where >> appears at the end of the type — the parser splits >> into two closing angle brackets rather than treating it as the right-shift operator.
Basic Nesting
Section titled “Basic Nesting”// Box<Box<int>> — the >> is parsed as two closing >var inner = new Box<int>(42)var outer = new Box<Box<int>>(inner)println(outer.get().get()) // 42Deep Nesting
Section titled “Deep Nesting”// Triple nestingvar deep = new Box<Box<Box<int>>>(new Box<Box<int>>(new Box<int>(99)))println(deep.get().get().get()) // 99Mixed Type Parameters
Section titled “Mixed Type Parameters”// Pair with nested generics in both positionsvar p = new Pair<Box<int>, Box<string>>(new Box<int>(10), new Box<string>("world"))println(p.first.get()) // 10println(p.second.get()) // worldUsing After Access
Section titled “Using After Access”Nested generic values work normally in expressions:
var b = new Box<Box<int>>(new Box<int>(5))var val: int = b.get().get() + 10println(val) // 15Combining Generics with Other Features
Section titled “Combining Generics with Other Features”Generics + Nullable Types
Section titled “Generics + Nullable Types”interface Finder<T> { find(id: int): T?}Generics + Async
Section titled “Generics + Async”class AsyncStore<T> { public async get(id: int): Task<T?> { // async lookup... return null }
public async save(item: T): Task<bool> { // async save... return true }}Generics + Abstract Classes
Section titled “Generics + Abstract Classes”abstract class BaseService<T> { abstract public async findById(id: int): Task<any> abstract public async create(data: T): Task<any>
public async exists(id: int): Task<bool> { var item: any = await this.findById(id) return item != null }}
class UserService extends BaseService<User> { override public async findById(id: int): Task<any> { // implementation... return null }
override public async create(data: User): Task<any> { // implementation... return null }}Complete Example
Section titled “Complete Example”// A generic stack data structure
class Stack<T> { var items: []any
constructor() { this.items = [] }
public push(item: T): void { this.items.push(item) }
public pop(): any { if (length(this.items) == 0) { return null } var last: any = this.items[length(this.items) - 1] this.items = slice(this.items, 0, length(this.items) - 1) return last }
public peek(): any { if (length(this.items) == 0) { return null } return this.items[length(this.items) - 1] }
public size(): int { return length(this.items) }
public isEmpty(): bool { return length(this.items) == 0 }}
// Usagevar intStack = new Stack<int>()intStack.push(10)intStack.push(20)intStack.push(30)println(intStack.peek()) // 30println(intStack.pop()) // 30println(intStack.size()) // 2
var strStack = new Stack<string>()strStack.push("hello")strStack.push("world")println(strStack.pop()) // worldSummary
Section titled “Summary”| Feature | Syntax | Example |
|---|---|---|
| Generic class | class Name<T> | class Box<T> { ... } |
| Multiple params | class Name<A, B> | class Pair<A, B> { ... } |
| Generic dataType | dataType Name<T> | dataType Point<T> { x: T; y: T } |
| Generic interface | interface Name<T> | interface Repo<T> { ... } |
| Generic function | function name<T>(...) | function identity<T>(v: T): T |
| Generic method | public name<T>(...) | public get<T>(key: string): any |
| Generic extends | extends Base<T> | class UserRepo extends Repository<User> |
| Instantiation | new Name<T>(...) | new Box<int>(42) |
| Generic call | obj.method<T>(...) | db.define<User>("users", ...) |
| Task type | Task<T> | async fn(): Task<string> |
| Array type | []T | var items: []int |
| Map type | map[K]V | var m: map[string]int |