@nest-boot/eslint-plugin 7.0.3 → 7.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/rules/graphql/graphql-field-config-from-types.d.ts +3 -1
  4. package/dist/rules/graphql/graphql-field-config-from-types.js +36 -36
  5. package/dist/rules/graphql/graphql-field-config-from-types.js.map +1 -1
  6. package/dist/rules/graphql/graphql-field-definite-assignment.d.ts +3 -1
  7. package/dist/rules/graphql/graphql-field-definite-assignment.js +13 -13
  8. package/dist/rules/graphql/graphql-field-definite-assignment.js.map +1 -1
  9. package/dist/rules/import/import-bullmq.d.ts +3 -1
  10. package/dist/rules/import/import-bullmq.js +4 -4
  11. package/dist/rules/import/import-bullmq.js.map +1 -1
  12. package/dist/rules/import/import-graphql.d.ts +3 -1
  13. package/dist/rules/import/import-graphql.js +4 -4
  14. package/dist/rules/import/import-graphql.js.map +1 -1
  15. package/dist/rules/import/import-mikro-orm.d.ts +3 -1
  16. package/dist/rules/import/import-mikro-orm.js +4 -4
  17. package/dist/rules/import/import-mikro-orm.js.map +1 -1
  18. package/dist/rules/index.d.ts +21 -7
  19. package/dist/rules/mikro-orm/entity-field-definite-assignment.d.ts +3 -1
  20. package/dist/rules/mikro-orm/entity-field-definite-assignment.js +12 -10
  21. package/dist/rules/mikro-orm/entity-field-definite-assignment.js.map +1 -1
  22. package/dist/rules/mikro-orm/entity-property-config-from-types.d.ts +3 -1
  23. package/dist/rules/mikro-orm/entity-property-config-from-types.js +149 -116
  24. package/dist/rules/mikro-orm/entity-property-config-from-types.js.map +1 -1
  25. package/dist/tsconfig.build.tsbuildinfo +1 -1
  26. package/dist/utils/createRule.d.ts +3 -1
  27. package/dist/utils/decorators.d.ts +16 -16
  28. package/dist/utils/decorators.js +16 -16
  29. package/package.json +12 -7
  30. package/src/rules/graphql/graphql-field-config-from-types.spec.ts +18 -18
  31. package/src/rules/graphql/graphql-field-config-from-types.ts +37 -37
  32. package/src/rules/graphql/graphql-field-definite-assignment.spec.ts +11 -11
  33. package/src/rules/graphql/graphql-field-definite-assignment.ts +13 -13
  34. package/src/rules/import/import-bullmq.spec.ts +9 -9
  35. package/src/rules/import/import-bullmq.ts +5 -4
  36. package/src/rules/import/import-graphql.spec.ts +8 -8
  37. package/src/rules/import/import-graphql.ts +4 -4
  38. package/src/rules/import/import-mikro-orm.spec.ts +8 -8
  39. package/src/rules/import/import-mikro-orm.ts +4 -4
  40. package/src/rules/mikro-orm/entity-field-definite-assignment.spec.ts +18 -18
  41. package/src/rules/mikro-orm/entity-field-definite-assignment.ts +12 -10
  42. package/src/rules/mikro-orm/entity-property-config-from-types.spec.ts +24 -24
  43. package/src/rules/mikro-orm/entity-property-config-from-types.ts +171 -117
  44. package/src/utils/decorators.ts +16 -16
@@ -42,16 +42,16 @@ exports.default = (0, createRule_1.createRule)({
42
42
  meta: {
43
43
  type: "problem",
44
44
  docs: {
45
- description: "根据 TypeScript 类型自动生成或修正 @Property 装饰器的类型与 nullable 配置(支持数组),以及 @Enum 装饰器的 nullable 配置。检查有初始化值的属性是否使用 Opt<T> 类型。",
45
+ description: "Automatically generate or fix @Property decorator type and nullable configuration based on TypeScript types (with array support), as well as @Enum decorator nullable configuration. Checks whether properties with initializers use the Opt<T> type.",
46
46
  },
47
47
  fixable: "code",
48
48
  schema: [],
49
49
  messages: {
50
- alignPropertyDecoratorWithTsType: "@Property 装饰器应与 TypeScript 类型保持一致(类型与 nullable)。",
51
- removePropertyDecorator: "属性带有 @{{decoratorName}} 装饰器,应移除 @Property 装饰器。",
52
- useEnumDecorator: "枚举类型应使用 @Enum 装饰器而不是 @Property 装饰器。",
53
- useOptTypeForInitializedProperty: "有非 null 初始化值的属性应使用 Opt<T> 类型包装。",
54
- removeOptTypeForNonInitializedProperty: "没有初始化值或初始化为 null 的属性不应使用 Opt<T> 类型包装。",
50
+ alignPropertyDecoratorWithTsType: "@Property decorator should align with the TypeScript type (type and nullable).",
51
+ removePropertyDecorator: "Property has a @{{decoratorName}} decorator, @Property decorator should be removed.",
52
+ useEnumDecorator: "Enum types should use @Enum decorator instead of @Property decorator.",
53
+ useOptTypeForInitializedProperty: "Properties with non-null initializers should use the Opt<T> type wrapper.",
54
+ removeOptTypeForNonInitializedProperty: "Properties without initializers or initialized to null should not use the Opt<T> type wrapper.",
55
55
  },
56
56
  },
57
57
  defaultOptions: [],
@@ -59,16 +59,16 @@ exports.default = (0, createRule_1.createRule)({
59
59
  const source = context.sourceCode;
60
60
  const parserServices = utils_1.ESLintUtils.getParserServices(context);
61
61
  const checker = parserServices.program.getTypeChecker();
62
- // 检查是否为枚举类型
62
+ // Check if the type is an enum
63
63
  const isEnumType = (node) => {
64
64
  try {
65
65
  const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
66
66
  const type = checker.getTypeAtLocation(tsNode);
67
- // 检查是否是枚举类型
67
+ // Check if it is an enum type
68
68
  if (type.symbol.flags & ts.SymbolFlags.Enum) {
69
69
  return true;
70
70
  }
71
- // 检查联合类型中的每个成员
71
+ // Check each member in a union type
72
72
  if (type.isUnion()) {
73
73
  return type.types.some((t) => t.symbol.flags & ts.SymbolFlags.EnumMember ||
74
74
  t.symbol.flags & ts.SymbolFlags.Enum);
@@ -85,15 +85,15 @@ exports.default = (0, createRule_1.createRule)({
85
85
  switch (typeName) {
86
86
  case "String":
87
87
  case "string":
88
- return "t.string"; // 默认使用 t.string
88
+ return "t.string"; // Default to t.string
89
89
  case "Number":
90
90
  case "number":
91
- return "t.float"; // 默认使用 t.float
91
+ return "t.float"; // Default to t.float
92
92
  case "Boolean":
93
93
  case "boolean":
94
- return "t.boolean"; // 默认使用 t.boolean
94
+ return "t.boolean"; // Default to t.boolean
95
95
  case "Date":
96
- return "t.datetime"; // 默认使用 t.datetime
96
+ return "t.datetime"; // Default to t.datetime
97
97
  case "GraphQLJSONObject":
98
98
  case "Record":
99
99
  return "t.json";
@@ -104,27 +104,28 @@ exports.default = (0, createRule_1.createRule)({
104
104
  const isValidStringType = (typeConfig) => {
105
105
  if (!typeConfig)
106
106
  return false;
107
- // 接受 t.string, t.text, t.decimal, t.bigint
107
+ // Accept t.string, t.text, t.uuid, t.decimal, t.bigint
108
108
  if (typeConfig === "t.string" ||
109
109
  typeConfig === "t.text" ||
110
+ typeConfig === "t.uuid" ||
110
111
  typeConfig === "t.decimal" ||
111
112
  typeConfig === "t.bigint") {
112
113
  return true;
113
114
  }
114
- // 接受 DecimalType(类引用,不带参数)
115
+ // Accept DecimalType (class reference, without arguments)
115
116
  if (typeConfig === "DecimalType") {
116
117
  return true;
117
118
  }
118
- // 接受 new DecimalType('string') new BigIntType('string')
119
- // 但排除 new BigIntType('number')
119
+ // Accept new DecimalType('string') and new BigIntType('string')
120
+ // but exclude new BigIntType('number')
120
121
  if (typeConfig.includes("DecimalType") ||
121
122
  typeConfig.includes("BigIntType")) {
122
- // 如果是 new BigIntType('number'),则无效
123
+ // If it's new BigIntType('number'), it's invalid
123
124
  if (typeConfig.includes("BigIntType") &&
124
125
  (typeConfig.includes("'number'") || typeConfig.includes('"number"'))) {
125
126
  return false;
126
127
  }
127
- // 其他情况(包括 new XXXType('string') DecimalType)都有效
128
+ // Other cases (including new XXXType('string') and DecimalType) are valid
128
129
  return true;
129
130
  }
130
131
  return false;
@@ -132,27 +133,27 @@ exports.default = (0, createRule_1.createRule)({
132
133
  const isValidNumberType = (typeConfig) => {
133
134
  if (!typeConfig)
134
135
  return false;
135
- // 接受 t.integer, t.float, t.double, t.decimal
136
+ // Accept t.integer, t.float, t.double, t.decimal
136
137
  if (typeConfig === "t.integer" ||
137
138
  typeConfig === "t.float" ||
138
139
  typeConfig === "t.double" ||
139
140
  typeConfig === "t.decimal") {
140
141
  return true;
141
142
  }
142
- // 接受 BigIntType DecimalType(类引用,不带参数)
143
+ // Accept BigIntType and DecimalType (class reference, without arguments)
143
144
  if (typeConfig === "BigIntType" || typeConfig === "DecimalType") {
144
145
  return true;
145
146
  }
146
- // 接受 new DecimalType('number') new BigIntType('number')
147
- // 但排除 new XXXType('string')
147
+ // Accept new DecimalType('number') and new BigIntType('number')
148
+ // but exclude new XXXType('string')
148
149
  if (typeConfig.includes("DecimalType") ||
149
150
  typeConfig.includes("BigIntType")) {
150
- // 如果包含 'string',则对 number 类型无效
151
+ // If it contains 'string', it's invalid for number type
151
152
  if (typeConfig.includes("'string'") ||
152
153
  typeConfig.includes('"string"')) {
153
154
  return false;
154
155
  }
155
- // 其他情况(包括 new XXXType('number'))都有效
156
+ // Other cases (including new XXXType('number')) are valid
156
157
  return true;
157
158
  }
158
159
  return false;
@@ -160,15 +161,15 @@ exports.default = (0, createRule_1.createRule)({
160
161
  const isValidNumberArrayType = (typeConfig) => {
161
162
  if (!typeConfig)
162
163
  return false;
163
- // 接受 t.array(默认)
164
+ // Accept t.array (default)
164
165
  if (typeConfig === "t.array") {
165
166
  return true;
166
167
  }
167
- // 接受 VectorType(类引用)
168
+ // Accept VectorType (class reference)
168
169
  if (typeConfig === "VectorType") {
169
170
  return true;
170
171
  }
171
- // 接受 new VectorType(...)
172
+ // Accept new VectorType(...)
172
173
  if (typeConfig.includes("VectorType")) {
173
174
  return true;
174
175
  }
@@ -177,7 +178,7 @@ exports.default = (0, createRule_1.createRule)({
177
178
  const isValidDateType = (typeConfig) => {
178
179
  if (!typeConfig)
179
180
  return false;
180
- // 接受 t.datetime, t.date, t.time
181
+ // Accept t.datetime, t.date, t.time
181
182
  if (typeConfig === "t.datetime" ||
182
183
  typeConfig === "t.date" ||
183
184
  typeConfig === "t.time") {
@@ -208,7 +209,7 @@ exports.default = (0, createRule_1.createRule)({
208
209
  node.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
209
210
  node.typeName.name === "Collection");
210
211
  };
211
- // 检查类型是否被 Opt<T> 包装
212
+ // Check if the type is wrapped with Opt<T>
212
213
  const isWrappedWithOpt = (property) => {
213
214
  const typeAnnotation = property.typeAnnotation;
214
215
  if (typeAnnotation?.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
@@ -219,13 +220,13 @@ exports.default = (0, createRule_1.createRule)({
219
220
  typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
220
221
  typeNode.typeName.name === "Opt");
221
222
  };
222
- // 检查是否已导入 Opt
223
+ // Check if Opt is imported
223
224
  const hasOptImport = () => {
224
225
  const program = context.sourceCode.ast;
225
226
  for (const statement of program.body) {
226
227
  if (statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration) {
227
228
  const importSource = statement.source.value;
228
- // 检查从 @mikro-orm/* 导入的语句
229
+ // Check imports from @mikro-orm/*
229
230
  if (typeof importSource === "string" &&
230
231
  importSource.startsWith("@mikro-orm/")) {
231
232
  const hasOpt = statement.specifiers.some((spec) => {
@@ -240,10 +241,10 @@ exports.default = (0, createRule_1.createRule)({
240
241
  }
241
242
  return false;
242
243
  };
243
- // 添加 Opt @mikro-orm/core 的导入
244
+ // Add Opt to the @mikro-orm/core import
244
245
  const addOptImport = (fixer) => {
245
246
  const program = context.sourceCode.ast;
246
- // 查找 @mikro-orm/core 的导入语句
247
+ // Find the @mikro-orm/core import statement
247
248
  let coreImport = null;
248
249
  for (const statement of program.body) {
249
250
  if (statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration) {
@@ -255,23 +256,23 @@ exports.default = (0, createRule_1.createRule)({
255
256
  }
256
257
  }
257
258
  if (coreImport) {
258
- // 已有 @mikro-orm/core 导入,添加 Opt 到导入列表
259
+ // Already has @mikro-orm/core import, add Opt to the import list
259
260
  const lastSpecifier = coreImport.specifiers[coreImport.specifiers.length - 1];
260
- // 检查是否是多行导入
261
+ // Check if it's a multiline import
261
262
  const importText = source.getText(coreImport);
262
263
  const isMultiline = importText.includes("\n");
263
264
  if (isMultiline) {
264
- // 多行导入:在最后一个导入项后添加,保持缩进
265
- const indent = " "; // 假设使用 2 个空格缩进
265
+ // Multiline import: add after the last import item, keeping indentation
266
+ const indent = " "; // Assuming 2-space indentation
266
267
  return fixer.insertTextAfter(lastSpecifier, `,\n${indent}Opt`);
267
268
  }
268
269
  else {
269
- // 单行导入:直接添加
270
+ // Single-line import: add directly
270
271
  return fixer.insertTextAfter(lastSpecifier, ", Opt");
271
272
  }
272
273
  }
273
274
  else {
274
- // 没有 @mikro-orm/core 导入,在文件开头添加新的导入语句
275
+ // No @mikro-orm/core import, add a new import statement at the top
275
276
  const firstImport = program.body.find((node) => node.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
276
277
  if (firstImport) {
277
278
  return fixer.insertTextBefore(firstImport, "import { Opt } from '@mikro-orm/core';\n");
@@ -286,11 +287,11 @@ exports.default = (0, createRule_1.createRule)({
286
287
  let baseTypeNode = property.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation
287
288
  ? property.typeAnnotation.typeAnnotation
288
289
  : null;
289
- // 可选属性(?)视为可空
290
+ // Optional property (?) is treated as nullable
290
291
  if (property.optional) {
291
292
  isNullable = true;
292
293
  }
293
- // 处理联合类型中的 null/undefined
294
+ // Handle null/undefined in union types
294
295
  if (baseTypeNode?.type === utils_1.AST_NODE_TYPES.TSUnionType) {
295
296
  const hasNullish = baseTypeNode.types.some((t) => {
296
297
  return (t.type === utils_1.AST_NODE_TYPES.TSNullKeyword ||
@@ -304,7 +305,7 @@ exports.default = (0, createRule_1.createRule)({
304
305
  t.type !== utils_1.AST_NODE_TYPES.TSUndefinedKeyword);
305
306
  }) ?? null;
306
307
  }
307
- // 先解包 Ref<T> Opt<T> → T,并在内部再次处理 null/undefined
308
+ // First unwrap Ref<T> and Opt<T> → T, and handle null/undefined within
308
309
  if (baseTypeNode?.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
309
310
  baseTypeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
310
311
  (baseTypeNode.typeName.name === "Ref" ||
@@ -325,11 +326,11 @@ exports.default = (0, createRule_1.createRule)({
325
326
  }
326
327
  baseTypeNode = inner ?? baseTypeNode;
327
328
  }
328
- // 跳过 Collection<T> 类型(这些应该由 OneToMany 等装饰器处理)
329
+ // Skip Collection<T> types (these should be handled by OneToMany etc. decorators)
329
330
  if (baseTypeNode && isCollectionType(baseTypeNode)) {
330
331
  return null;
331
332
  }
332
- // 数组类型(T[] Array<T>)- 在解包 Opt/Ref 之后检查
333
+ // Array type (T[] or Array<T>) - checked after unwrapping Opt/Ref
333
334
  const elementTypeNode = baseTypeNode
334
335
  ? extractArrayElementType(baseTypeNode)
335
336
  : null;
@@ -337,11 +338,11 @@ exports.default = (0, createRule_1.createRule)({
337
338
  isArray = true;
338
339
  }
339
340
  const targetTypeNode = elementTypeNode ?? baseTypeNode;
340
- // 检查目标类型是否为枚举
341
+ // Check if target type is an enum
341
342
  if (targetTypeNode) {
342
343
  isEnum = isEnumType(targetTypeNode);
343
344
  }
344
- // 无显式类型时,尝试从字面量初始值推断
345
+ // When no explicit type, try to infer from literal initializer
345
346
  if (!targetTypeNode) {
346
347
  if (property.value?.type === utils_1.AST_NODE_TYPES.Literal) {
347
348
  const value = property.value.value;
@@ -389,7 +390,7 @@ exports.default = (0, createRule_1.createRule)({
389
390
  arrayElementTypeName: isArray ? "Record" : undefined,
390
391
  };
391
392
  }
392
- // 关键字类型
393
+ // Keyword types
393
394
  if (targetTypeNode.type === utils_1.AST_NODE_TYPES.TSStringKeyword) {
394
395
  return {
395
396
  typeName: "string",
@@ -420,10 +421,10 @@ exports.default = (0, createRule_1.createRule)({
420
421
  arrayElementTypeName: isArray ? "boolean" : undefined,
421
422
  };
422
423
  }
423
- // 标识符(类/自定义类型)
424
+ // Identifier (class/custom type)
424
425
  const ident = getIdentifierName(targetTypeNode);
425
426
  if (ident) {
426
- // 如果是数组,自定义类型数组使用 t.json
427
+ // For arrays, custom type arrays use t.json
427
428
  if (isArray) {
428
429
  return {
429
430
  typeName: ident,
@@ -445,7 +446,7 @@ exports.default = (0, createRule_1.createRule)({
445
446
  }
446
447
  return null;
447
448
  };
448
- // 将类型包装为 Opt<T>
449
+ // Wrap type with Opt<T>
449
450
  const wrapWithOpt = (typeString) => {
450
451
  return `Opt<${typeString}>`;
451
452
  };
@@ -462,13 +463,13 @@ exports.default = (0, createRule_1.createRule)({
462
463
  }
463
464
  return `@Enum({ ${options.join(", ")} })`;
464
465
  };
465
- const buildPropertyDecorator = (info, otherProps = []) => {
466
+ const buildPropertyDecorator = (info, otherProps = [], decoratorName = "Property") => {
466
467
  const options = [];
467
- // 如果有 propertyType 配置,添加 type
468
+ // If there is a propertyType configuration, add type
468
469
  if (info.propertyType) {
469
470
  options.push(`type: ${info.propertyType}`);
470
471
  }
471
- // 添加其他属性(保持原有顺序)
472
+ // Add other properties (keeping original order)
472
473
  for (const prop of otherProps) {
473
474
  options.push(`${prop.key}: ${prop.value}`);
474
475
  }
@@ -476,9 +477,9 @@ exports.default = (0, createRule_1.createRule)({
476
477
  options.push("nullable: true");
477
478
  }
478
479
  if (options.length === 0) {
479
- return "@Property()";
480
+ return `@${decoratorName}()`;
480
481
  }
481
- return `@Property({ ${options.join(", ")} })`;
482
+ return `@${decoratorName}({ ${options.join(", ")} })`;
482
483
  };
483
484
  const addPropertyDecorator = (property, info) => {
484
485
  const fixes = [];
@@ -490,33 +491,39 @@ exports.default = (0, createRule_1.createRule)({
490
491
  });
491
492
  return fixes;
492
493
  };
493
- const fixWithTypeInfo = (property, propertyDecorator, info, currentConfig) => {
494
+ const fixWithTypeInfo = (property, propertyDecorator, info, currentConfig, decoratorName = "Property") => {
494
495
  const fixes = [];
495
- // 如果当前已经有有效的配置,保留它而不是替换成默认值
496
+ // If current config already has a valid value, keep it instead of replacing with default
496
497
  const finalInfo = { ...info };
498
+ // PrimaryKey number type defaults to t.integer
499
+ if (decoratorName === "PrimaryKey" &&
500
+ finalInfo.propertyType === "t.float") {
501
+ finalInfo.propertyType = "t.integer";
502
+ }
497
503
  if (info.propertyType === "t.string" &&
498
504
  isValidStringType(currentConfig.type)) {
499
- // 保留有效的 string 类型配置
505
+ // Keep valid string type config
500
506
  finalInfo.propertyType = currentConfig.type;
501
507
  }
502
- else if (info.propertyType === "t.float" &&
508
+ else if ((info.propertyType === "t.float" ||
509
+ info.propertyType === "t.integer") &&
503
510
  isValidNumberType(currentConfig.type)) {
504
- // 保留有效的 number 类型配置
511
+ // Keep valid number type config
505
512
  finalInfo.propertyType = currentConfig.type;
506
513
  }
507
514
  else if (info.propertyType === "t.datetime" &&
508
515
  isValidDateType(currentConfig.type)) {
509
- // 保留有效的 Date 类型配置
516
+ // Keep valid Date type config
510
517
  finalInfo.propertyType = currentConfig.type;
511
518
  }
512
519
  else if (info.isArray &&
513
520
  info.arrayElementTypeName === "number" &&
514
521
  isValidNumberArrayType(currentConfig.type)) {
515
- // 保留有效的 number[] 类型配置(VectorType
522
+ // Keep valid number[] type config (VectorType)
516
523
  finalInfo.propertyType = currentConfig.type;
517
524
  }
518
- // 保留其他属性配置
519
- const newDecoratorText = buildPropertyDecorator(finalInfo, currentConfig.otherProps);
525
+ // Keep other property configs
526
+ const newDecoratorText = buildPropertyDecorator(finalInfo, currentConfig.otherProps, decoratorName);
520
527
  fixes.push({
521
528
  type: "replace",
522
529
  range: propertyDecorator.range,
@@ -565,7 +572,7 @@ exports.default = (0, createRule_1.createRule)({
565
572
  }
566
573
  }
567
574
  else {
568
- // 保留其他所有属性
575
+ // Keep all other properties
569
576
  otherProps.push({
570
577
  key: prop.key.name,
571
578
  value: source.getText(prop.value),
@@ -603,38 +610,38 @@ exports.default = (0, createRule_1.createRule)({
603
610
  }
604
611
  return { items, nullable };
605
612
  };
606
- // 检查文件中是否使用了 Opt 类型但没有导入
613
+ // Check if the file uses the Opt type but hasn't imported it
607
614
  const checkOptUsageWithoutImport = (node) => {
608
615
  let usesOpt = false;
609
- // 遍历所有成员,检查是否有使用 Opt<T> 的类型注解
616
+ // Iterate through all members to check for Opt<T> type annotations
610
617
  node.body.body.forEach((member) => {
611
618
  if (member.type !== utils_1.AST_NODE_TYPES.PropertyDefinition)
612
619
  return;
613
620
  const typeAnnotation = member.typeAnnotation?.typeAnnotation;
614
621
  if (!typeAnnotation)
615
622
  return;
616
- // 递归检查类型节点中是否包含 Opt
623
+ // Recursively check if the type node contains Opt
617
624
  const containsOpt = (typeNode) => {
618
625
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
619
626
  if (!typeNode)
620
627
  return false;
621
- // 检查是否是 Opt<T>
628
+ // Check if it's Opt<T>
622
629
  if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
623
630
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
624
631
  typeNode.typeName?.type === utils_1.AST_NODE_TYPES.Identifier &&
625
632
  typeNode.typeName.name === "Opt") {
626
633
  return true;
627
634
  }
628
- // 递归检查联合类型
635
+ // Recursively check union types
629
636
  if (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType) {
630
637
  return typeNode.types.some((t) => containsOpt(t));
631
638
  }
632
- // 递归检查类型参数
639
+ // Recursively check type parameters
633
640
  if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
634
641
  typeNode.typeArguments?.params) {
635
642
  return typeNode.typeArguments.params.some((param) => containsOpt(param));
636
643
  }
637
- // 递归检查数组元素类型
644
+ // Recursively check array element type
638
645
  if (typeNode.type === utils_1.AST_NODE_TYPES.TSArrayType) {
639
646
  return containsOpt(typeNode.elementType);
640
647
  }
@@ -644,7 +651,7 @@ exports.default = (0, createRule_1.createRule)({
644
651
  usesOpt = true;
645
652
  }
646
653
  });
647
- // 如果使用了 Opt 但没有导入,报告错误
654
+ // If Opt is used but not imported, report an error
648
655
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
649
656
  if (usesOpt && !hasOptImport()) {
650
657
  context.report({
@@ -661,19 +668,18 @@ exports.default = (0, createRule_1.createRule)({
661
668
  ClassDeclaration(node) {
662
669
  if (!isEntityClass(node))
663
670
  return;
664
- // 首先检查是否使用了 Opt 但没有导入
671
+ // First check if Opt is used but not imported
665
672
  checkOptUsageWithoutImport(node);
666
673
  node.body.body.forEach((member) => {
667
674
  if (member.type !== utils_1.AST_NODE_TYPES.PropertyDefinition)
668
675
  return;
669
- // 检查关系装饰器(PrimaryKey, OneToOne, OneToMany, ManyToOne, ManyToMany
676
+ // Check relation decorators (OneToOne, OneToMany, ManyToOne, ManyToMany)
670
677
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
671
678
  const hasRelationDecorator = member.decorators?.some((decorator) => {
672
679
  if (decorator.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
673
680
  decorator.expression.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
674
681
  const decoratorName = decorator.expression.callee.name;
675
682
  return [
676
- "PrimaryKey",
677
683
  "OneToOne",
678
684
  "OneToMany",
679
685
  "ManyToOne",
@@ -682,41 +688,63 @@ exports.default = (0, createRule_1.createRule)({
682
688
  }
683
689
  return false;
684
690
  });
685
- // 如果有关系装饰器,跳过检查(这些属性不需要 @Property 装饰器)
691
+ // If there are relation decorators, skip checking (these properties don't need @Property)
686
692
  if (hasRelationDecorator)
687
693
  return;
688
- // 检查 @Enum 装饰器
694
+ // Property-like decorators (decorators that need type checking)
695
+ const propertyLikeDecorators = [
696
+ "Property",
697
+ "PrimaryKey",
698
+ "EncryptedProperty",
699
+ "HashedProperty",
700
+ ];
701
+ // Find property-like decorator
689
702
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
690
- const enumDecorator = member.decorators?.find((decorator) => {
691
- return (decorator.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
692
- decorator.expression.callee.type ===
693
- utils_1.AST_NODE_TYPES.Identifier &&
694
- decorator.expression.callee.name === "Enum");
703
+ const propertyLikeDecorator = member.decorators?.find((decorator) => {
704
+ if (decorator.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
705
+ decorator.expression.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
706
+ return propertyLikeDecorators.includes(decorator.expression.callee.name);
707
+ }
708
+ return false;
695
709
  });
710
+ // Get decorator name
711
+ const getDecoratorName = (decorator) => {
712
+ if (decorator.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
713
+ decorator.expression.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
714
+ return decorator.expression.callee.name;
715
+ }
716
+ return null;
717
+ };
718
+ // Check @Enum decorator
696
719
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
697
- const propertyDecorator = member.decorators?.find((decorator) => {
720
+ const enumDecorator = member.decorators?.find((decorator) => {
698
721
  return (decorator.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
699
722
  decorator.expression.callee.type ===
700
723
  utils_1.AST_NODE_TYPES.Identifier &&
701
- decorator.expression.callee.name === "Property");
724
+ decorator.expression.callee.name === "Enum");
702
725
  });
726
+ // Use property-like decorator
727
+ const propertyDecorator = propertyLikeDecorator;
728
+ const currentDecoratorName = propertyDecorator
729
+ ? getDecoratorName(propertyDecorator)
730
+ : null;
703
731
  const typeInfo = computeTypeInfo(member);
704
732
  if (!typeInfo?.typeName)
705
733
  return;
706
- // 检查初始化值和 Opt<T> 类型的匹配
734
+ // Check initializer and Opt<T> type match
707
735
  const hasInitializer = member.value !== null;
708
736
  const isOptWrapped = isWrappedWithOpt(member);
709
- // 检查初始化值是否为 null
737
+ // Check if the initializer is null
710
738
  const isInitializedToNull = hasInitializer &&
711
739
  member.value?.type === utils_1.AST_NODE_TYPES.Literal &&
712
740
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
713
741
  member.value?.value === null;
714
- // 情况 1:有非 null 初始化值但没有使用 Opt<T>
742
+ // Case 1: Has non-null initializer but not wrapped with Opt<T>
715
743
  if (hasInitializer &&
716
744
  !isInitializedToNull &&
717
745
  !isOptWrapped &&
718
746
  !member.optional) {
719
- // 有初始化值但没有使用 Opt<T> 包装,需要报错
747
+ // Has initializer but not wrapped with Opt<T>, needs to report error
720
748
  const typeAnnotation = member.typeAnnotation?.typeAnnotation;
721
749
  const needsImport = !hasOptImport();
722
750
  if (typeAnnotation) {
@@ -729,7 +757,7 @@ exports.default = (0, createRule_1.createRule)({
729
757
  const fixes = [
730
758
  fixer.replaceText(typeAnnotation, wrappedType),
731
759
  ];
732
- // 如果需要,添加 Opt 导入
760
+ // If needed, add Opt import
733
761
  if (needsImport) {
734
762
  const importFix = addOptImport(fixer);
735
763
  if (importFix)
@@ -740,7 +768,7 @@ exports.default = (0, createRule_1.createRule)({
740
768
  });
741
769
  }
742
770
  else if (member.value) {
743
- // 没有类型注解,从初始化值推断类型
771
+ // No type annotation, infer type from initializer
744
772
  let inferredType = null;
745
773
  if (member.value.type === utils_1.AST_NODE_TYPES.Literal) {
746
774
  const valueType = typeof member.value.value;
@@ -752,11 +780,11 @@ exports.default = (0, createRule_1.createRule)({
752
780
  inferredType = "string";
753
781
  }
754
782
  else if (member.value.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
755
- // 空数组 [] 的情况,需要从 @Property 装饰器推断类型
783
+ // Empty array [] case, need to infer type from @Property decorator
756
784
  inferredType = "unknown[]";
757
785
  }
758
786
  else if (member.value.type === utils_1.AST_NODE_TYPES.NewExpression) {
759
- // new Date() 的情况
787
+ // new Date() case
760
788
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
761
789
  if (member.value.callee?.type === utils_1.AST_NODE_TYPES.Identifier) {
762
790
  inferredType = member.value.callee.name;
@@ -772,7 +800,7 @@ exports.default = (0, createRule_1.createRule)({
772
800
  const fixes = [
773
801
  fixer.insertTextAfter(propertyName, `: ${wrappedType}`),
774
802
  ];
775
- // 如果需要,添加 Opt 导入
803
+ // If needed, add Opt import
776
804
  if (needsImport) {
777
805
  const importFix = addOptImport(fixer);
778
806
  if (importFix)
@@ -784,10 +812,10 @@ exports.default = (0, createRule_1.createRule)({
784
812
  }
785
813
  }
786
814
  }
787
- // 如果有 @Enum 装饰器,无论是否识别为枚举类型,都检查其配置
815
+ // If there is an @Enum decorator, check its configuration regardless of whether the type is recognized as enum
788
816
  if (enumDecorator) {
789
817
  const enumConfig = parseEnumDecorator(enumDecorator);
790
- // 检查 nullable 配置是否与 TypeScript 类型匹配
818
+ // Check if nullable configuration matches the TypeScript type
791
819
  if (enumConfig.nullable !== typeInfo.isNullable) {
792
820
  const expectedEnumText = buildEnumDecorator(typeInfo);
793
821
  context.report({
@@ -798,12 +826,12 @@ exports.default = (0, createRule_1.createRule)({
798
826
  },
799
827
  });
800
828
  }
801
- // @Enum 装饰器的属性,不需要 @Property 装饰器
829
+ // Properties with @Enum decorator do not need @Property decorator
802
830
  return;
803
831
  }
804
- // 如果是枚举类型但没有 @Enum 装饰器
832
+ // If it's an enum type but doesn't have @Enum decorator
805
833
  if (typeInfo.isEnum) {
806
- // 如果有 @Property 装饰器,建议替换为 @Enum
834
+ // If it has @Property decorator, suggest replacing with @Enum
807
835
  if (propertyDecorator) {
808
836
  context.report({
809
837
  node: propertyDecorator,
@@ -815,7 +843,7 @@ exports.default = (0, createRule_1.createRule)({
815
843
  });
816
844
  return;
817
845
  }
818
- // 如果没有 @Enum 装饰器,添加它
846
+ // If it doesn't have @Enum decorator, add one
819
847
  context.report({
820
848
  node: member,
821
849
  messageId: "useEnumDecorator",
@@ -826,7 +854,7 @@ exports.default = (0, createRule_1.createRule)({
826
854
  });
827
855
  return;
828
856
  }
829
- // 非枚举类型:如果没有 @Property 装饰器,添加它
857
+ // Non-enum type: if no @Property decorator, add one
830
858
  if (!propertyDecorator) {
831
859
  context.report({
832
860
  node: member,
@@ -838,34 +866,39 @@ exports.default = (0, createRule_1.createRule)({
838
866
  });
839
867
  return;
840
868
  }
841
- // 检查现有 @Property 装饰器是否与类型匹配
869
+ // Check if existing decorator matches the type
842
870
  const currentConfig = parsePropertyDecorator(propertyDecorator);
843
- const expectedType = typeInfo.propertyType;
871
+ let expectedType = typeInfo.propertyType;
872
+ // PrimaryKey number type expects t.integer
873
+ if (currentDecoratorName === "PrimaryKey" &&
874
+ expectedType === "t.float") {
875
+ expectedType = "t.integer";
876
+ }
844
877
  let needReport = false;
845
- // 检查 type 配置
878
+ // Check type configuration
846
879
  if (expectedType && currentConfig.type !== expectedType) {
847
- // 对于 string 类型,接受多种有效配置
880
+ // For string type, accept multiple valid configurations
848
881
  if (expectedType === "t.string" &&
849
882
  isValidStringType(currentConfig.type)) {
850
- // 当前配置是有效的 string 类型配置,不需要修改
883
+ // Current config is a valid string type configuration, no modification needed
851
884
  }
852
- else if (expectedType === "t.float" &&
885
+ else if ((expectedType === "t.float" || expectedType === "t.integer") &&
853
886
  isValidNumberType(currentConfig.type)) {
854
- // 当前配置是有效的 number 类型配置,不需要修改
887
+ // Current config is a valid number type configuration, no modification needed
855
888
  }
856
889
  else if (expectedType === "t.datetime" &&
857
890
  isValidDateType(currentConfig.type)) {
858
- // 当前配置是有效的 Date 类型配置,不需要修改
891
+ // Current config is a valid Date type configuration, no modification needed
859
892
  }
860
893
  else if (expectedType === "t.array" &&
861
894
  typeInfo.arrayElementTypeName === "number" &&
862
895
  isValidNumberArrayType(currentConfig.type)) {
863
- // 当前配置是有效的 number[] 类型配置(t.array VectorType),不需要修改
896
+ // Current config is a valid number[] type configuration (t.array or VectorType), no modification needed
864
897
  }
865
898
  else if (expectedType === "t.json") {
866
- // 对于 t.json 类型(Record 或自定义类型数组)
899
+ // For t.json type (Record or custom type arrays)
867
900
  if (currentConfig.type === "t.json") {
868
- // t.json 配置正确,不需要修改
901
+ // t.json config is correct, no modification needed
869
902
  }
870
903
  else {
871
904
  needReport = true;
@@ -876,10 +909,10 @@ exports.default = (0, createRule_1.createRule)({
876
909
  }
877
910
  }
878
911
  else if (!expectedType && currentConfig.type) {
879
- // 如果不需要 type 但当前有 type,也需要修正
912
+ // If type is not needed but currently has one, needs correction
880
913
  needReport = true;
881
914
  }
882
- // 检查 nullable 配置
915
+ // Check nullable configuration
883
916
  if (currentConfig.nullable !== typeInfo.isNullable) {
884
917
  needReport = true;
885
918
  }
@@ -889,7 +922,7 @@ exports.default = (0, createRule_1.createRule)({
889
922
  node: member,
890
923
  messageId: "alignPropertyDecoratorWithTsType",
891
924
  fix: (fixer) => {
892
- const fixes = fixWithTypeInfo(member, propertyDecorator, typeInfo, currentConfig);
925
+ const fixes = fixWithTypeInfo(member, propertyDecorator, typeInfo, currentConfig, currentDecoratorName ?? "Property");
893
926
  return applyFixes(fixer, fixes);
894
927
  },
895
928
  });