Examples

Entity.of typer function (t) => type allows you to type properties in a very simple, familiar and composable way. That means you can achieve deeply nested and complex types via the function composition pattern:

t.array(t.record(String, t.union(String, Number, Boolean)))

t.record(String, t.record(String, t.record(String, t.record(String, String))))

Bellow you can find examples of how to use the @Of decorator with some of the commonly encountered usecases.


Primitives

String

@Of((t) => t(String))
prop: string = '';

Number

@Of((t) => t(Number))
prop: number = 0;

Boolean

@Of((t) => t(Boolean))
prop: boolean = false;

Entities

@Of((t) => t(SomeEntity))
prop: SomeEntity = SomeEntity.of({});

Records

Records of primitives

@Of((t) => t.record(String, String))
prop: Record<string, string> = {};

@Of((t) => t.record(String, Number))
prop: Record<string, number> = {};

@Of((t) => t.record(String, Boolean))
prop: Record<string, boolean> = {};

Records of entities

@Of((t) => t.record(String, SomeEntity))
prop: Record<string, SomeEntity> = {};

Records of records

@Of((t) => t.record(String, t.record(String, String)))
prop: Record<string, Record<string, string>> = {};

@Of((t) => t.record(String, t.record(String, Number)))
prop: Record<string, Record<string, number>> = {};

@Of((t) => t.record(String, t.record(String, Boolean)))
prop: Record<string, Record<string, boolean>> = {};

Records of arrays

@Of((t) => t.record(String, t.array(String)))
prop: Record<string, string[]> = {};

@Of((t) => t.record(String, t.array(Number)))
prop: Record<string, number[]> = {};

@Of((t) => t.record(String, t.array(Boolean)))
prop: Record<string, boolean[]> = {};

Records of unions

@Of((t) => t.record(String, t.union(String, Number, Boolean)))
prop: Record<string, string | number | boolean> = {};

Arrays

@Of((t) => t.array(String))
prop: string[] = [];

@Of((t) => t.array(Number))
prop: number[] = [];

@Of((t) => t.array(Boolean))
prop: boolean[] = [];

Arrays of entities

@Of((t) => t.array(SomeEntity))
prop: SomeEntity[] = [];

Arrays of records

@Of((t) => t.array(t.record(String, String)))
prop: Record<string, string>[] = [];

@Of((t) => t.array(t.record(String, Number)))
prop: Record<string, number>[] = [];

@Of((t) => t.array(t.record(String, Boolean)))
prop: Record<string, boolean>[] = [];

Arrays of arrays

@Of((t) => t.array(t.array(String)))
prop: string[][] = [];

@Of((t) => t.array(t.array(Number)))
prop: number[][] = [];

@Of((t) => t.array(t.array(Boolean)))
prop: boolean[][] = [];

Arrays of unions

@Of((t) => t.array(t.union(String, Number, Boolean)))
prop: (string | number | boolean)[] = [];

Unions

@Of((t) => t.union(String, Number, Boolean))
prop: string | number | boolean = '';

Unions of entities

@Of((t) => t.union(SomeEntity, SomeOtherEntity))
prop: SomeEntity | SomeOtherEntity = SomeEntity.of({});

@Of((t) => t.union(SomeEntity, t.record(String, String)))
prop: SomeEntity | Record<string, string> = {};

Unions of records

@Of((t) => t.union(t.record(String, String), t.record(String, Number), t.record(String, Boolean)))
prop: Record<string, string> | Record<string, number> | Record<string, boolean> = {};

Unions of arrays

@Of((t) => t.union(t.array(String), t.array(Number), t.array(Boolean)))
prop: string[] | number[] | boolean[] = [];

Optional & Nullable

In order to make a property optional or nullable, you can use the optional() and nullable() methods chainable on every type returned by the typer function.

// Primitives
@Of((t) => t(String).optional())
prop?: string;

@Of((t) => t(Number).nullable())
prop: number | null = null;

@Of((t) => t(Boolean).optional().nullable())
prop?: number | null;

// Entities
@Of((t) => t(SomeEntity).optional())
prop?: SomeEntity;

@Of((t) => t(SomeEntity).nullable())
prop: SomeEntity | null = null;

@Of((t) => t(SomeEntity).optional().nullable())
prop?: SomeEntity | null;

// Records
@Of((t) => t.record(String, String).optional())
prop?: Record<string, string>;

@Of((t) => t.record(String, Number).nullable())
prop: Record<string, number> | null = null;

@Of((t) => t.record(String, Boolean).optional().nullable())
prop?: Record<string, boolean> | null;

// Arrays
@Of((t) => t.array(String).optional())
prop?: string[];

@Of((t) => t.array(Number).nullable())
prop: number[] | null = null;

@Of((t) => t.array(Boolean).optional().nullable())
prop?: boolean[] | null;

// Unions
@Of((t) => t.union(String, Number, Boolean).optional())
prop?: string | number | boolean;

@Of((t) => t.union(String, Number, Boolean).nullable())
prop: string | number | boolean | null = null;

@Of((t) => t.union(String, Number, Boolean).optional().nullable())
prop?: string | number | boolean | null;

Custom

For advanced usecases that are not possible to achieve with the built in types, you can use the custom() method. This method takes a function that receives the value and returns the transformed value. Think of it as a map() function for the raw value that’s going to be passed for the property.

@Of((t) => t.custom((value) => {
  if (Object.hasOwn(value, 'foo')) {
    return SomeEntity.of(value);
  }
  return SomeOtherEntity.of(value);
}))
prop: SomeEntity | SomeOtherEntity = SomeEntity.of({});