typescript-expression-language

A TypeScript-native implementation of the Symfony Expression Language. Zero dependencies, ESM, CommonJS and UMD output, and a bounded LRU cache. Everything below runs the published package, loaded straight from unpkg.

Result

        
Compiled

        

Quick start

Install

npm install @andreasnicolaou/typescript-expression-language

ESM

import { ExpressionLanguage } from "@andreasnicolaou/typescript-expression-language";

const el = new ExpressionLanguage();

el.evaluate("user.age >= 18 and user.active", {
  user: { age: 30, active: true },
});

Browser

<script src="https://unpkg.com/@andreasnicolaou/typescript-expression-language/dist/index.umd.min.js"></script>
<script>
  const { ExpressionLanguage } = typescriptExpressionLanguage;
  new ExpressionLanguage().evaluate("1 + 2 * 3");
</script>

evaluate() runs an expression directly. compile() returns the equivalent source instead, and lint() validates without running.

Syntax

+ - * / % **
Arithmetic. 1 / 0 and 10 % 0 throw rather than returning Infinity or NaN.
== === != !==
Loose and strict comparison, alongside < > <= >=.
and or not xor
Logic, with && || ! available as symbol forms.
& | ^ << >>
Bitwise operators. Unary ~ is bitwise not.
~
String concatenation, using JavaScript coercion.
matches
Tests against a JavaScript regular expression, written as a delimited literal such as '/^\d{4}$/'.
starts with
Also ends with and contains.
in
Membership, with not in for the inverse.
..
Range, so 1..5 yields [1, 2, 3, 4, 5].
? : ?: ?? ?.
Ternary, elvis, null-coalescing and null-safe access.
[] {}
Array and hash literals, indexed with items[0] and user.name.
1_000_000
Numeric separators, plus scientific and leading-dot forms such as .5 and 1.5e2.
/* … */
Block comments, ignored by the lexer.
min max constant
Registered by default, along with enum, isset and now.

Providers

PHP-named helpers are not registered by default. They ship behind a separate /providers entry point so the core bundle stays lean, and they carry no runtime dependencies either.

import { ExpressionLanguage } from "@andreasnicolaou/typescript-expression-language";
import { StringProvider } from "@andreasnicolaou/typescript-expression-language/providers";

const el = new ExpressionLanguage(undefined, [new StringProvider()]);
el.evaluate('strtoupper(substr(name, 0, 3))', { name: "andreas" });
MathProvider
abs, ceil, floor, round, sqrt, pow
StringProvider
strtolower, strtoupper, strlen, trim, ltrim, rtrim, ucfirst, lcfirst, ucwords, strrev, explode, str_replace, substr
ArrayProvider
count, implode, array_keys, array_values, array_merge, array_reverse, array_unique, array_sum, in_array, array_intersect, array_slice
DateProvider
checkdate, date, date_parse, gmdate, gmmktime, mktime, strtotime, time — using PHP second timestamps

Symfony compatibility

Operators, precedence, literals and error behavior follow Symfony's ExpressionLanguage rather than JavaScript's where the two disagree. String literals unescape the same way, so '\\' is one backslash while '\d' stays intact and regex patterns survive.

Values stay JavaScript-native by design: numbers are IEEE-754 doubles, matches uses JavaScript regular expressions rather than PCRE, and ~ concatenates with JavaScript coercion.

GitHub · npm · Changelog