Struct is a Typescript utility library for defining and instantiating typed and prefilled Structs and validating and asserting objects and primitives.
Structs are factories for objects. You can instantiate a struct by either providing a type for an empty struct, the default values for a defaulted struct, or a type for the struct and default values for a partially pre-instantiated struct.
Structs with complete default values can infer their type from the provided defaults.
const Vec2 = Struct({ x: 0, y: 0 }); // Creates a struct factory with default values
const position = new Vec2(); // { x: 0, y: 0}
A struct with no default values should be provided a type signature.
type Vec3 = {x: number, y: number, z: number};
const Vec3 = Struct<Vec3>();
const position = new Vec3({
x: 1,
y: 5,
z: 2
}); // Must provide all required fields.
For partial default values you must provide a type for both the entire struct and the default values <StructType, DefaultStructValues>
. This is due to a limitation of Typescript regarding partial generics.
type Momentum = Vec3 & {velocity: number};
const Momentum = Struct<Momentum, { velocity: number }>({ velocity: 0 });
// <Type of Struct, Default values>
const m = new Momentum({
x: 2,
y: 3,
z: 2
// velocity not required for instantiation
}); // { x: 2, y: 3, z: 2, velocity: 0 }
If you have a Struct, you can infer it's type using the InferStructType
utility type.
const Euler = Struct({ x: 0, y: 0, z: 0, order: "XYZ" });
type Euler = InferStructType<Euler>; // { x: number, y: number, z: number, order: string }
You can infer the required properties of a struct from the struct type and default property type.
type Texture = {
x: number,
y: number,
repeat: number,
data: Uint8Array
};
type TextureDefaults = { x: number, y: number, repeat: number };
type RequiredTextureProps = RequiredProperties<
Texture, TextureDefaults
>; // { data: Uint8Array }
const Texture = Struct<Texture, RequiredTextureProps>({
x: 0, y: 0, repeat: 0
});
const tex = new Texture({
data: textureData
});
Validated Structs work similarly to Structs, but use a schema defined with regular Javascript and are validated on creation an property assignment.
const Vec2 = VStruct({
x: Number,
y: Number
});
const pos = new Vec2({
x: 0, y: 2
}); // { x: 0, y: 2}
const User = VStruct({
id: Number,
name: String,
roles: Array(Number),
address: {
street: Optional(String),
country: Union(Country, Number)
},
info: Any,
isAdmin: Optional(Nullable(Boolean)),
data: Unknown,
nonUserProperty: Never,
});
const user = new User(possibleUser); // throws if invalid user;
user.address = "123 American St"; // throws
String: string
Number: number
Boolean: boolean
BigInt: bigint
Symbol: symbol
Date: DateConstructor
Object: ObjectConstructor
Function: FunctionConstructor
Map: MapConstructor
Set: SetConstructor
WeakMap: WeakMapConstructor
WeakSet: WeakSetConstructor
ArrayBuffer: ArrayBufferConstructor
DataView: DataViewConstructor
Int8Array: Int8ArrayConstructor
Uint8Array: Uint8ArrayConstructor
Uint8ClampedArray: Uint8ClampedArrayConstructor
Int16Array: Int16ArrayConstructor
Uint16Array: Uint16ArrayConstructor
Int32Array: Int32ArrayConstructor
Uint32Array: Uint32ArrayConstructor
Float32Array: Float32ArrayConstructor
Float64Array: Float64ArrayConstructor;
undefined: undefined
null: null
Union(Number, String): number | string
Nullable(Function): Function | null
Optional(String): string?
Any: any
Unknown: unknown
Never: never
If you have a VStruct, you can infer it's type using the InferVStructType
utility type.
const Light = VStruct({
position: {
x: Number, y: Number, z: Number,
},
intensity: Optional(Nullable(Number))
});
type Light = InferVStructType<Light>;
// { position: { x: number, y: number, z: number}, intensity?: number | null | undefined }