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 literals —
Environment = \dev` | `staging` | `prod`` defines an exact set of allowed values. - Generic type —
Result<T>parameterizes over any typeT. The compiler infersT = ServiceConfigfrom usage. - Constrained/defaulted generic alias —
Field<T, K extends keyof T = `name`>restrictsKto valid keys and letsField<ServiceConfig>omit the trailing key argument. - Field annotations on object types —
ServiceConfig.nameandServiceConfig.replicascarry reusable@constraintrules that travel with the type. - Utility types preserve field annotations —
Pick<ServiceConfig, ...>andPartial<ServiceConfig>reuse the original field metadata when those fields survive the transform. - Optional field —
data?: Tmeans the field may be absent. When present its type isT; when absent it reads asnull. matchexhaustiveness — the compiler rejects amatchthat does not cover all variants of a union.
Run it
rank examples/types-and-generics