Types and Values
lash is dynamically typed. Variables do not carry type annotations; their type is determined at runtime by the value they hold.
Supported Types
Section titled “Supported Types”| Type | Example |
|---|---|
string | let greeting = "hello" |
int | let count = 42 |
float | let ratio = 3.14 |
bool | let verbose = true |
list | let files = ["a.txt", "b.txt"] |
object | let config = { editor: "vim", tabs: 4 } |
semver | let ver = semver("1.2.3") |
Type Bounds
Section titled “Type Bounds”Every type defines .min and .max properties:
| Type | .min | .max |
|---|---|---|
int | -9223372036854775808 | 9223372036854775807 |
float | -1.7976931348623157e+308 | 1.7976931348623157e+308 |
string | "" (empty string) | undefined |
bool | false | true |
list | [] (empty list) | undefined |
object | {} (empty object) | undefined |
Semver Type
Section titled “Semver Type”The semver() constructor parses a semantic version string and returns a tagged object:
let ver = semver("1.2.3-beta+build.42")| Field | Type | Description |
|---|---|---|
major | int | Major version number |
minor | int | Minor version number |
patch | int | Patch version number |
prerelease | string | Pre-release identifier (may be "") |
build | string | Build metadata (may be "") |
The object carries a hidden __type entry (value "semver") excluded from keys() output. Comparison operators follow semver 2.0 precedence rules. String representation: MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD].
User-Defined Types
Section titled “User-Defined Types”In addition to the built-in types, scripts and plugins can declare their own types with type Name { ... }. A user-defined type isn’t a new runtime value shape — values remain strings — but it carries validation and completion metadata that drive typed parameter checking and tab-complete:
/// A local or remote git branch name.type GitBranch { cache = 2s values(query) { `git branch -a --format=%(refname:short)` } validate(x) { x in values("") } // optional}| Clause | Purpose |
|---|---|
values(query) | Required for completion types. Returns a list of candidate values, optionally filtered by the user’s current prefix. Feeds tab completion. |
validate(x) | Optional. Returns a boolean for whether x is acceptable. When omitted, lash derives it as x in values(""). |
cache = <duration> | Optional. Memoizes values(query) per-query for the given TTL. Units: ms, s, m, h, d. Errors are never cached. |
template = "path" | Optional. Binds the type to a text template. Constructing the type renders the template with the literal’s fields; fields are inferred from the template body. See Typed Templates. |
A function parameter typed with a user-defined type (fn gco(ref: GitBranch) { ... }) runs validate on the raw argument; a failure produces 'X' is not a valid GitBranch. In a union (GitBranch | int), the arms are tried in source order — the first one whose validate succeeds wins.
A type with a template clause is a different beast: it doesn’t carry validation or completion — its fields are inferred from the template, and constructing it produces a renderable value with implicit .render() and .write(stream) methods. See Typed Templates for the full feature.
See Defining a custom type for completion for a worked completion-type example, and Generate config from typed templates for a worked template-type example.
Universal Properties
Section titled “Universal Properties”These properties are available on all value types:
.isSuccess— Returnstrueif the value is “successful”. For command capture results (raw or.captureobjects), checks exit code == 0. For other values, returns truthiness..isFailure— The inverse of.isSuccess.
Collection Literals
Section titled “Collection Literals”List Literals
Section titled “List Literals”let items = [1, 2, 3]let mixed = ["hello", 42, true]Lists can hold values of any type, including mixed types.
Object Literals
Section titled “Object Literals”let config = { host: "localhost", port: 8080 }Object Property Access
Section titled “Object Property Access”Bracket syntax and dot syntax are both supported:
config["host"] # bracket accessconfig.host # dot access (when key is a valid identifier)Dot access on objects resolves the key name as a property lookup. It must not collide with built-in method names — use bracket syntax when a key matches a method name.
Range Expressions
Section titled “Range Expressions”let nums = 1..10 # produces [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Ranges produce integer lists and are usable anywhere a list is expected.