为什么你的项目值得拥有自己的语言?
\n\n想象一下:你正在编写计算保险费的代码,不再需要几十个嵌套的if-else和魔法数字,而是看到简洁的结构:
const rule = new InsuranceRule()\n .when(age > 25)\n .and(experience > 2)\n .then(premium = base * 0.8);\n\n这不是魔法。这是DSL(领域特定语言)——一种嵌入在TypeScript中的领域特定语言。在本文中,我们将探讨如何创建这样的语言,让你的代码用业务语言说话,而不是机器语言。
\n\n什么是DSL,为什么需要它?
\n\nDSL是为解决特定领域问题而创建的语言。与通用语言(Java、C#、TypeScript)不同,DSL不追求通用性。它牺牲灵活性以换取表现力。
\n\n何时应该使用DSL:
\n- \n
- 业务逻辑经常变化,需要让分析师能够修改 \n
- 项目中有大量重复的配置模式 \n
- 需要将复杂算法与其余代码隔离 \n
- 需要在语法层面进行数据验证 \n
TypeScript是创建DSL的理想选择。静态类型、强大的类型系统以及编译为JavaScript的能力,使其成为构建内部和外部语言的灵活工具。
\n\n内部DSL:流畅API与链式调用
\n\n在TypeScript中创建DSL最简单的方法是使用Fluent Interface(流畅接口)。即每个方法都返回对象自身,从而允许构建调用链。
\n\n示例:SQL查询构造器
\n\nclass QueryBuilder {\n private table: string = '';\n private conditions: string[] = [];\n private selectedFields: string[] = ['*'];\n\n select(...fields: string[]): this {\n this.selectedFields = fields.length ? fields : ['*'];\n return this;\n }\n\n from(table: string): this {\n this.table = table;\n return this;\n }\n\n where(condition: string): this {\n this.conditions.push(condition);\n return this;\n }\n\n and(condition: string): this {\n return this.where(condition);\n }\n\n build(): string {\n let sql = `SELECT ${this.selectedFields.join(', ')} FROM ${this.table}`;\n if (this.conditions.length) {\n sql += ` WHERE ${this.conditions.join(' AND ')}`;\n }\n return sql;\n }\n}\n\n// 使用\nconst query = new QueryBuilder()\n .select('name', 'age')\n .from('users')\n .where('age > 18')\n .and('status = "active"')\n .build();\n\nconsole.log(query);\n// SELECT name, age FROM users WHERE age > 18 AND status = "active"\n\n这是经典的TypeScript内部DSL。它不需要单独的解析器,直接在TypeScript代码中工作。主要优点是完全支持自动补全和类型化。
\n\n进阶:类型化的业务规则DSL
\n\n使用泛型和联合类型添加严格类型化:
\n\ntype ComparisonOperator = '>' | '<' | '=' | '!=';\n\nclass RuleBuilder<T extends Record<string, any>> {\n private conditions: ((data: T) => boolean)[] = [];\n\n when<K extends keyof T>(field: K, op: ComparisonOperator, value: T[K]): this {\n this.conditions.push((data: T) => {\n switch (op) {\n case '>': return data[field] > value;\n case '<': return data[field] < value;\n case '=': return data[field] === value;\n case '!=': return data[field] !== value;\n }\n });\n return this;\n }\n\n and<K extends keyof T>(field: K, op: ComparisonOperator, value: T[K]): this {\n return this.when(field, op, value);\n }\n\n evaluate(data: T): boolean {\n return this.conditions.every(c => c(data));\n }\n}\n\n// 用于检查的数据类型\ninterface UserData