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.
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 / 0and10 % 0throw 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 withandcontains. - in
- Membership, with
not infor the inverse. - ..
- Range, so
1..5yields[1, 2, 3, 4, 5]. - ? : ?: ?? ?.
- Ternary, elvis, null-coalescing and null-safe access.
- [] {}
- Array and hash literals, indexed with
items[0]anduser.name. - 1_000_000
-
Numeric separators, plus scientific and leading-dot forms such as
.5and1.5e2. - /* … */
- Block comments, ignored by the lexer.
- min max constant
- Registered by default, along with
enum,issetandnow.
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.