Osy#betaRun it on Osyrin: osyrin.com

Reference / Types

Every type, in one list

The complete vocabulary of built-in types — the scalars you can store, the collections, the two callable spellings (`Action` and `Func`), and the wrappers a component parameter may take. If a type is not on this page and you did not declare it yourself, it does not exist.

stable4 examples compiled by CItypesguidereference

Summary#

Types explains the types with a wrinkle. This page is the other half: the complete list, so that "does the language have a type for this?" is a question you can answer by looking rather than by guessing.

It is worth having because guessing goes wrong in a specific way — you invent a name for something that already exists. There is no Command type for a callable; it is Action. There is no Set<T>; it is HashSet<T>. Every name below is one the compiler matches, and the list is checked against the compiler itself, so it cannot quietly fall behind.

Anything not on this page is a type you declare — an entity, an enum, a class, or a component's own type parameter.

Description#

Scalars and value kinds#

The storable types. These are what an entity member, a function local, or a component parameter may be.

TypeWhat it is
stringText. default(string) is null, exactly as in C#.
charA single character, in single quotes. See char.
intA 32-bit integer.
longA 64-bit integer. See long.
doubleA double-precision float — measurements, science.
boolTrue or false.
decimalExact base-10 — money, quantities. See decimal.
bigdecimalExact base-10 with 38 significant digits, for when decimal's ~28 are not enough. See bigdecimal.
DateTimeA date and time. See DateTime.
DateTimeOffsetAn instant together with its UTC offset. See DateTimeOffset.
DateOnlyA calendar date with no time of day.
TimeOnlyA time of day with no date.
TimeSpanA duration. See TimeSpan (durations).
GuidA globally unique id.
JsonAn arbitrary JSON document.
RichTextFormatted prose, stored as a rich-text document.
MarkdownA section-addressable markdown document. See Markdown.
VectorAn embedding, for similarity search; [MaxLength] sets the dimensions.
TimeZoneInfoA time zone, stored as its IANA id, e.g. Europe/Stockholm — C#'s TimeZoneInfo.
CultureA BCP-47 culture token, e.g. sv-SE.
GeographyA point, line or polygon on the earth, as GeoJSON. See Geography.

A bare member of one of these is required unless the type has an honest zero — see Optional and required members, which is the rule that decides whether ? is needed.

Which collection types may I write?#

You writeWhat it is
T[]An array.
T?[]An array whose ELEMENTS may be null — string?[], decimal?[]. (T[]? is an array that may itself be null.)
T[][]A JAGGED array — a collection of collections, which is how you spell a grid. Indexes as g[y][x], on both sides of an assignment. T[,] (rectangular) is not a type here, and the compiler says so and points at this form.
byte[]Binary data — the one array form that is a scalar rather than a collection.
List<T>An ordered, mutable list.
HashSet<T>A set of distinct values.
Dictionary<K, V>A keyed map.
stream<T>A collection that is still being written. A function returning one produces its results with yield return, and a live var bound to it renders each item as it arrives — see yield — a function that produces results over time.

An entity's child rows are not one of these — they are a collection property on the parent, described in relations. Reach for List<T> for an in-memory list, never to hold children.

A jagged array is not a separate kind. T[] is a collection of T, so T[][] is a collection of those. The outer rank is a List, so a grid can be appended to (board.Add(row)) and a List<T[]> goes straight into one — a bare T[] value does not, for the reason every T[] → List<T> is refused: an array cannot promise the .Add the slot offers. So build the grid as a List<T[]> and assign it as it stands, without a .ToArray().

int[][] Grid(int w) {
  var rows = new List<int[]>();
  for (var y = 0; y < w; y = y + 1) {
    var row = new List<int>();
    for (var x = 0; x < w; x = x + 1) { row.Add(0); }
    rows.Add(row.ToArray());
  }
  int[][] board = rows;                     // the List goes straight in — no `.ToArray()` on the outer rank
  board[0][0] = 1;                          // write a cell
  return board;
}

int[][] Laid() { int[][] board = [[1, 2], [3, 4]]; return board; }   // …or laid out literally

new T[n] — n copies of the default#

new T[n] is an array of n copies of default(T), as in C#. For a number that is 0, for a bool false, for a char '\0', for a DateTime DateTime.MinValue, for a TimeSpan TimeSpan.Zero, and for an enum its zero member. For a string or a class it is null, so new string[3] is a string?[]. A slot you have not written reads as null, so read it with ?? "" or check it for null first. The same rule holds in a function and in the browser.

string Labels() {
  var names = new string[3];
  names[0] = "first";
  names[2] = "third";
  return string.Join(",", names.Select(n => n ?? "-"));   // "first,-,third"
}

⚠ int[,] does not exist. A rectangular array is a distinct type in C# and not one Osy# has; the compiler refuses it by name and tells you to write int[][], which indexes identically.

How do I type a function value?#

Two spellings, both exactly C#'s, and there are no others:

You writeWhat it is
ActionA callback that takes nothing and returns nothing.
Action<T…>A callback that takes arguments and returns nothing.
Func<T…, TResult>A callback that returns a value. The last type argument is the return type.
component PrimaryButton(string label, Action onPress) { … }

class MenuEntry {
  public string Label;
  public Action Run;          // the verb to run — an Action, not a "Command"
}

Func with no type argument is an error: a function that returns something must say what. For a callback that returns nothing, use Action.

Calling one. A callable is invoked exactly as in C# — run() on a local or parameter, entry.Run() on a class member, and Run() unqualified inside the class that declares it:

class MenuEntry {
  public string Label;
  public Action Run;
}

component Menu(MenuEntry[] entries, Action onDismiss) {
  action Choose(MenuEntry entry) {
    entry.Run();      // run the verb the caller put on this entry
    onDismiss();      // and the callback this component was given
  }
  …
}

The argument count and types are checked against the callable's signature, so Action<int> invoked with a string is a compile error rather than a surprise on the client.

A verb call evaluates to nothing. It is fire-and-forget: the call does not wait for the verb to finish and yields no value, so a Func<…, T> cannot be invoked — the compiler says so by name rather than handing you a value that never arrives. Use Action for a callback and a plain function when you want a result.

Component-parameter wrappers#

Writable on a component parameter, where they mean something the plain type cannot say:

You writeWhat it is
Binding<T>A two-way binding — the component reads and writes the caller's value.
Query<T>A reactive query handle the component re-reads as the data changes.
ContentAn opaque children slot — whatever the caller nests inside.
SlotA named children slot. See Slot (child content).
component TypeDropdown(Binding<OrganizationType> value) { … }

Reading a value out of text#

int.Parse, long.Parse, decimal.Parse, double.Parse and bool.Parse read a value of their type out of a string, as C# does. Numbers are read in the current culture, as C#'s are: the viewer's, else app.DefaultCulture, else the invariant culture (1234.5) — so under sv-SE decimal.Parse("1 234,5") is 1234.5 and "1234.5" is an error. Pass CultureInfo.InvariantCulture to read text a machine wrote (decimal.Parse(s, CultureInfo.InvariantCulture)). The same text gives the same number on the server and in the browser (Culture formatting — ToString(format, culture)). bool.Parse takes true or false in any letter case. Text that is not a value of the type is an error, never a quiet 0 or false: catch it where a person typed the text, and let it fail where your own code produced it.

A double also holds IEEE 754's non-numbers, spelled as in C#: double.NaN, double.PositiveInfinity, double.NegativeInfinity (and double.Epsilon, the smallest positive value). A NaN is equal to nothing, itself included, so x == double.NaN is always false — ask with double.IsNaN(x), double.IsInfinity(x), double.IsPositiveInfinity(x), double.IsNegativeInfinity(x) or double.IsFinite(x). They answer the same on the server, in the browser and in a stored query, and a query sorts NaN before every number, as C# does.

Types you declare#

Everything else is yours: an entity (persisted), an enum, a plain class (in-memory), and a component's own type parameters. Those are named by their declaration and scoped by namespace and type visibility (public / internal).

Examples#

Scalars and a collection, together in one function, so the spellings on this page are shown compiling rather than only described. (Callables belong to a component or a class, so their example lives in Callables above.)

string Summarise(string title, List<decimal> amounts) {
  decimal total = 0;
  foreach (var a in amounts) {
    if (a > 0) total = total + a;
  }
  return title + ": " + total;
}
// Summarise("Q1", [10, -5, 20])   ->  "Q1: 30"

Reading values out of text — the typed amount is caught, because a person typed it; the rest come from systems that promise the format, so a bad value fails loudly.

decimal? TypedAmount(string typed) {
  decimal? amount = null;
  try { amount = decimal.Parse(typed); } catch { }
  return amount;
}

long IssuedSequence(string issued) { return long.Parse(issued); }
double SensorReading(string text) { return double.Parse(text); }
bool FeatureFlag(string text) { return bool.Parse(text); }

See also#

Related

Types

The values your app computes with, and the declarations that name and scope them. Most scalar types are exactly C#'s —…

char

A single character, written in single quotes. It is what you get from `s[0]` and from iterating a string, and it is the…

entity members

The typed members an entity holds — text, numbers, dates, booleans, Guids, enums and references. A member's type…

Optional and required members

A member is required or optional by how you spell its type. A bare value type with a natural zero reads that zero; a…

When your name is already the platform's

The platform puts 70 ordinary English words in scope in every app with no `using` — `Group`, `Match`, `Point`, `Month`…

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

Classes

A class is an in-memory shape — data plus the behaviour that belongs to it — and it never touches the database. That is…