FlameWright: Runtime Type Validation¶
FlameWright (and FlameGuards) is the runtime type validation system in gaslamp.
Since Google Apps Script does not support TypeScript at runtime,
type checking must happen in code — and FlameWright is the tool for that.
Combined with FlameFrame, you can validate every column in a DataFrame in one step —
and when invalids are found, errors tells you exactly which row and field caused the problem.
FlameWright is most powerful when used with FlameFrame,
but it also works standalone on any value in your code.
// With FlameFrame: validate DataFrame columns
const { passed, failed } = gaslamp.FlameFrame.from(df, {
name: gaslamp.FlameGuards.isString,
age: gaslamp.FlameGuards.isNumber,
});
// Standalone: validate any value
if (!gaslamp.FlameGuards.isNumber(value)) {
throw new Error("Expected a number");
}
For applying a schema to DataFrame columns, see FlameFrame and Schema Validation.
Core Concept: Flame Guards¶
A Flame guard is a function that takes an unknown value and returns true or false.
const isString = (value) => typeof value === "string";
isString("hello"); // true
isString(42); // false
In GAS, use the predefined guards from FlameGuards or build your own with FlameWright.
FlameGuards — Predefined Guards¶
FlameGuards provides ready-to-use guards for common types.
Primitive guards¶
gaslamp.FlameGuards.isString("hello"); // true
gaslamp.FlameGuards.isNumber(42); // true — excludes NaN
gaslamp.FlameGuards.isBoolean(true); // true
gaslamp.FlameGuards.isNull(null); // true
gaslamp.FlameGuards.isUndefined(undefined); // true
Object guards¶
gaslamp.FlameGuards.isValidDate(new Date()); // true
gaslamp.FlameGuards.isArray([1, 2, 3]); // true
gaslamp.FlameGuards.isMap(new Map()); // true
gaslamp.FlameGuards.isPlainObject({ name: "Alice" }); // true
GAS sheet value guards¶
Sheet cells often contain empty strings or mixed types. These guards handle the most common cases.
gaslamp.FlameGuards.isEmptyString(""); // true — blank cell
gaslamp.FlameGuards.isStringOrNumber("Alice"); // true — typical cell value
gaslamp.FlameGuards.isStringOrNumber(42); // true — NaN also passes (use isNumber to exclude it)
gaslamp.FlameGuards.isTable([[1, 2], [3, 4]]); // true — getValues() output
FlameWright — Parameterized Validators¶
FlameWright builds validators for more complex types.
arrayOf — typed array¶
const isStringArray = gaslamp.FlameWright.arrayOf(gaslamp.FlameGuards.isString);
isStringArray(["Alice", "Bob"]); // true
isStringArray(["Alice", 42]); // false
enumOf — allowed values¶
const isStatus = gaslamp.FlameWright.enumOf(["active", "inactive"]);
isStatus("active"); // true
isStatus("deleted"); // false
inRange — number range¶
const isPercent = gaslamp.FlameWright.inRange(0, 100);
const isPositive = gaslamp.FlameWright.inRange(0);
isPercent(50); // true
isPercent(101); // false
isPositive(999); // true
nullable — allow null¶
const nullableString = gaslamp.FlameWright.nullable(gaslamp.FlameGuards.isString);
nullableString(null); // true — blank cell may come in as null
nullableString("abc"); // true
nullableString(42); // false
anyOf — union type¶
const isStringOrDate = gaslamp.FlameWright.anyOf([
gaslamp.FlameGuards.isString,
gaslamp.FlameGuards.isValidDate,
]);
isStringOrDate("2024-01-01"); // true
isStringOrDate(new Date()); // true
isStringOrDate(42); // false
fromSchema — object validator¶
const isUser = gaslamp.FlameWright.fromSchema({
name: gaslamp.FlameGuards.isString,
age: gaslamp.FlameGuards.isNumber,
});
isUser({ name: "Alice", age: 30 }); // true
isUser({ name: "Bob" }); // false — age missing
partial — optional fields¶
partial wraps each field to also accept undefined, then pass to fromSchema:
const partialSchema = gaslamp.FlameWright.partial({
name: gaslamp.FlameGuards.isString,
age: gaslamp.FlameGuards.isNumber,
});
const isPartialUser = gaslamp.FlameWright.fromSchema(partialSchema);
isPartialUser({ name: "Alice" }); // true — age is optional
isPartialUser({}); // true — all fields optional
isPartialUser({ name: 123 }); // false — name must still be a string
extend — merge schemas¶
const baseSchema = { name: gaslamp.FlameGuards.isString };
const extraSchema = { age: gaslamp.FlameGuards.isNumber };
const isUser = gaslamp.FlameWright.fromSchema(
gaslamp.FlameWright.extend(baseSchema, extraSchema)
);
isUser({ name: "Alice", age: 30 }); // true
FlameGaslets — GAS Object Guards¶
GAS Sheet and Spreadsheet objects cannot be reliably checked with instanceof
across script boundaries. FlameGaslets uses duck-typing instead.
function processInput(value) {
if (gaslamp.FlameGaslets.isSheet(value)) {
const df = gaslamp.BareFrame.fromSheet(value);
console.log(df.length);
return;
}
if (gaslamp.FlameGaslets.isSpreadsheet(value)) {
const sheet = value.getActiveSheet();
console.log(sheet.getName());
return;
}
throw new Error("Expected a Sheet or Spreadsheet object");
}
Validation Helpers¶
ensure — validate and return typed value¶
Use ensure when you need the validated value assigned to a variable.
Throws TypeError with message "ensure failed | @<at> | "<label>" | got: <value>".
function processRow(row) {
const name = gaslamp.ensure(
row.get("name"),
gaslamp.FlameGuards.isString,
{ at: "processRow", label: "name" },
);
// Throws: TypeError: ensure failed | @processRow | "name" | got: 123
console.log(name.toUpperCase()); // name is guaranteed to be a string
}
assert — narrow type in-place¶
Use assert when you want to narrow the type of an existing variable.
Throws TypeError with message "assert failed | @<at> | "<label>" | got: <value>".
function calcTotal(price) {
gaslamp.assert(
price,
gaslamp.FlameGuards.isNumber,
{ at: "calcTotal", label: "price" },
);
// Throws: TypeError: assert failed | @calcTotal | "price" | got: "abc"
return price * 1.1; // price is guaranteed to be a number
}
explain — field-level error messages¶
Use explain to get human-readable error messages per field.
Useful for logging validation problems when processing rows.
// Field present but wrong type → "invalid"
const errors = gaslamp.explain(
{ name: 123, age: 30 },
{ name: gaslamp.FlameGuards.isString, age: gaslamp.FlameGuards.isNumber },
);
// ["name: invalid (123)"]
// Field missing entirely → "missing"
const errors2 = gaslamp.explain(
{ name: "Alice" },
{ name: gaslamp.FlameGuards.isString, age: gaslamp.FlameGuards.isNumber },
);
// ["age: missing"]
if (errors.length > 0) {
console.error("Validation failed: " + errors.join(", "));
}
Practical Workflow¶
Validating function arguments¶
Use guards to protect any function from unexpected input:
function calculateDiscount(price, rate) {
gaslamp.assert(price, gaslamp.FlameWright.inRange(0), { at: "calculateDiscount", label: "price" });
gaslamp.assert(rate, gaslamp.FlameWright.inRange(0, 1), { at: "calculateDiscount", label: "rate" });
return price * (1 - rate);
}
Validating a GAS form submission¶
Validate user input from a GAS form before processing:
function onFormSubmit(e) {
const response = {
name: e.values[1],
email: e.values[2],
age: Number(e.values[3]),
};
const schema = {
name: gaslamp.FlameGuards.isString,
email: gaslamp.FlameGuards.isString,
age: gaslamp.FlameWright.inRange(0, 120),
};
const errors = gaslamp.explain(response, schema);
if (errors.length > 0) {
console.error("Invalid submission: " + errors.join(", "));
return;
}
// Safe to use response.name, response.email, response.age here
console.log(`Received from ${response.name}`);
}
Next Steps¶
- FlameFrame and Schema Validation — apply a schema to DataFrame columns
- API Reference — full reference for
FlameGuards,FlameWright,FlameGaslets