Converts a TypeScript lambda filter expression:
(person) => person.Age >= 18 && person.Name.startsWith('A');to an OData $filter string:
"Age gt 10 and startswith(Name, 'A')"
This project contains TypeScript Transformers capable of turning TypeScript lambda functions into OData $filter
strings. This allows you to have full type safety while creating an expression with all of the full type information,
but at runtime be working with a generated OData $filter string.
For example, consider the following nominal TypeScript:
import { serializeExpression } from 'ts-lambda-to-odata';
interface Person {
name: string;
age: number;
}
const result: string = serializeExpression<Person>((x: Person) => x.age > 10 && x.name == 'Bob');Note that the serializeExpression<T> function accepts a (x: T) => boolean filter lambda expression.
After compilation, the transpiled JavaScript becomes the following:
const result = `age gt 10 and name eq 'Bob'`;Note that the serializeExpression<T> does not actually exist in the final compiled JavaScript; instead, it acts as a
placeholder in TypeScript to be replaced with the OData $filter string at compile/transpile-time.
The strings also support external variables. The following TypeScript:
const num: number = 1234;
const result: string = serializeExpression<Person>(x => x.age <= num);becomes:
const num = 1234;
const result = `age le ${num}`;In a straight TypeScript project, simply add ts-lambda-to-odata and ts-loader to your project. In your
project's Webpack config, add the serializeExpressionTransformer as a custom transformer:
/** webpack.config.js */
const expressionSerializer = require('ts-lambda-to-odata/build-tools');
module.exports = {
// ...
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader', // or 'awesome-typescript-loader'
options: {
getCustomTransformers: program => ({
before: [expressionSerializer.serializeExpressionTransformer(program)],
}),
},
},
],
},
};In Angular land, it's actually a bit easier:
First, add ts-lambda-to-odata and @angular-builders/custom-webpack to your project.
In your angular.json file, change the builder to @angular-builders/custom-webpack, and then add a
customWebpackConfig TypeScript file:
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.ts"
},
// ... etcThen, in your custom Webpack config file:
// webpack.config.ts
import { Configuration } from 'webpack';
import { addSerializeExpressionTransformerToAngularPipeline } from 'ts-lambda-to-odata/build-tools';
export default (config: Configuration) => {
config = addSerializeExpressionTransformerToAngularPipeline(config);
return config;
};That's it! All calls to serializeExpression<T>() will be replaced with OData strings at runtime.
This library supports converting most standard TypeScript syntax into OData $filter grammar:
| Description | TypeScript syntax | OData $filter syntax |
|---|---|---|
| Equality | ==, ===, !=, !== |
eq, ne |
| Comparison | >, >=, <, <= |
gt, ge, lt, le |
| Grouping | &&, ||, (, ) |
and, or, (, ) |
| Negation | !expression |
not [expression] |
| Arithmetic | +, -, *, /, % |
add, sub, mul, div, mod |
| String literals | "string", 'string', `string` |
'string' |
| Number literals | 1234, 1.23 |
1234, 1.23 |
| Boolean literals | true, false |
true, false |
| Nullability literals | null, undefined |
null |
| String starts with | str.startsWith('x') |
startswith(str, 'x') |
| String ends with | str.endsWith('x') |
endswidth(str, 'x') |
| String index | str.indexOf('x') |
indexof(str, 'x') |
| String substring | str.substring(0, 2) |
substring(str, 0, 2) |
| String lowercase | str.toLowerCase('ABC') |
tolower(str, 'ABC') |
| String uppercase | str.toUpperCase('abc') |
toupper(str, 'abc') |
| String trim | str.trim(' hello ') |
trim(str, ' hello ') |
| String includes | str.includes('x') |
contains(str, 'x') |
| String length | str.length |
length(str) |
| Array contains | array.includes(3) |
contains(array, 3) |
| Array index | array.indexOf(5) |
indexof(arr, 5) |
| Array at least one | items.some(i => i.Value > 10) |
items/any(i: i/Value gt 10) |
| Array every | items.every(i => i.Value == 5) |
items/all(i: i/Value eq 5) |
| Array length | items.length |
length(items) |
npm test