Skip to main content

Types and Generics

Named type aliases, constrained and defaulted generics, utility types, field annotations on object types, literal unions, and exhaustive match expressions.

Source

examples/types-and-generics/main.rank
/// Demonstrates type aliases, constrained and defaulted generics, utility
/// types, field annotations on object types, literal unions, and exhaustive
/// match expressions.

use std::string::{ isBlank }
use std::types::{ Partial, Pick }

Environment = `dev` | `staging` | `prod`

LogLevel = `debug` | `info` | `warn` | `error`

Result<T> = Object {
ok: bool,
data?: T,
error?: string,
}

Field<T, K extends keyof T = `name`> = T[K]

ServiceConfig = Object {
@constraint(cond: |self| !isBlank(self))
name: string,
env: Environment,
@constraint(cond: |self| self >= 1)
replicas: number,
logLevel: LogLevel,
}

ServiceSummary = Pick<ServiceConfig, `name` | `logLevel`>
ServicePatch = Partial<ServiceConfig>

env: Environment = `staging`

log_level_for = |e: Environment| -> LogLevel {
return match e {
`dev` => `debug`,
`staging` => `info`,
`prod` => `warn`,
}
}

replicas_for = |e: Environment| -> number {
return match e {
`dev` => 1,
`staging` => 2,
`prod` => 5,
}
}

config: ServiceConfig = {
name: `api`,
env: env,
replicas: replicas_for(env),
logLevel: log_level_for(env),
}

service_name: Field<ServiceConfig> = config.name
replica_count: Field<ServiceConfig, `replicas`> = config.replicas

summary: ServiceSummary = {
name: service_name,
logLevel: config.logLevel,
}

patch: ServicePatch = {
replicas: replica_count,
}

result: Result<ServiceConfig> = {
ok: true,
data: config,
}

pub main = || result

Output

{
"ok": true,
"data": {
"name": "api",
"env": "staging",
"replicas": 2,
"logLevel": "info"
}
}

Key concepts

  • Union of literalsEnvironment = \dev` | `staging` | `prod`` defines an exact set of allowed values.
  • Generic typeResult<T> parameterizes over any type T. The compiler infers T = ServiceConfig from usage.
  • Constrained/defaulted generic aliasField<T, K extends keyof T = `name`> restricts K to valid keys and lets Field<ServiceConfig> omit the trailing key argument.
  • Field annotations on object typesServiceConfig.name and ServiceConfig.replicas carry reusable @constraint rules that travel with the type.
  • Utility types preserve field annotationsPick<ServiceConfig, ...> and Partial<ServiceConfig> reuse the original field metadata when those fields survive the transform.
  • Optional fielddata?: T means the field may be absent. When present its type is T; when absent it reads as null.
  • match exhaustiveness — the compiler rejects a match that does not cover all variants of a union.

Run it

rank examples/types-and-generics