eslint-plugin-class-validator-type-match 3.1.2 → 3.1.4

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.
@@ -73,7 +73,7 @@ exports.default = createRule({
73
73
  let checker = null;
74
74
  let esTreeNodeMap = null;
75
75
  try {
76
- const parserServices = context.parserServices;
76
+ const parserServices = context.sourceCode.parserServices;
77
77
  if (parserServices?.program && parserServices?.esTreeNodeToTSNodeMap) {
78
78
  checker = parserServices.program.getTypeChecker();
79
79
  esTreeNodeMap = parserServices.esTreeNodeToTSNodeMap;
@@ -131,7 +131,9 @@ exports.default = createRule({
131
131
  const enumArg = (0, type_helpers_util_1.getIsEnumArgument)(isEnumDecorator);
132
132
  const typeName = (0, type_helpers_util_1.getTypeReferenceName)(typeAnnotation.typeName);
133
133
  // For TypeScript enum references, the argument should match the type
134
- if (enumArg && enumArg !== typeName) {
134
+ // Skip this check if the argument is an array of enum values (subset pattern)
135
+ const isArraySubset = (0, type_helpers_util_1.isEnumArgumentArraySubset)(isEnumDecorator, checker, esTreeNodeMap);
136
+ if (enumArg && enumArg !== typeName && !isArraySubset) {
135
137
  context.report({
136
138
  node,
137
139
  messageId: 'enumMismatch',
@@ -6,6 +6,15 @@ const type_helpers_util_1 = require("../utils/type-helpers.util");
6
6
  * Creates an ESLint rule with proper documentation URL
7
7
  */
8
8
  const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com/robertlinde/eslint-plugin-class-validator-type-match#${name}`);
9
+ /**
10
+ * Maps JavaScript wrapper class names to their corresponding primitive type names.
11
+ * Used to validate @Type(() => Number) with `number` type, etc.
12
+ */
13
+ const PRIMITIVE_WRAPPER_MAP = {
14
+ Number: 'number',
15
+ String: 'string',
16
+ Boolean: 'boolean',
17
+ };
9
18
  /**
10
19
  * ESLint rule to ensure @Type(() => ClassName) decorator matches TypeScript type annotations.
11
20
  *
@@ -129,15 +138,18 @@ exports.default = createRule({
129
138
  }
130
139
  }
131
140
  else {
132
- // @Type is used with a primitive type
133
- context.report({
134
- node,
135
- messageId: 'typeMismatch',
136
- data: {
137
- typeDecoratorClass: typeClassName,
138
- actualType,
139
- },
140
- });
141
+ // @Type is used with a primitive type - check if it's a valid wrapper class match
142
+ const expectedPrimitive = PRIMITIVE_WRAPPER_MAP[typeClassName];
143
+ if (!expectedPrimitive || expectedPrimitive !== actualType) {
144
+ context.report({
145
+ node,
146
+ messageId: 'typeMismatch',
147
+ data: {
148
+ typeDecoratorClass: typeClassName,
149
+ actualType,
150
+ },
151
+ });
152
+ }
141
153
  }
142
154
  }
143
155
  // For array types, check if @Type matches the array element type
@@ -158,17 +170,20 @@ exports.default = createRule({
158
170
  }
159
171
  }
160
172
  else {
161
- // @Type is used with an array of primitives
173
+ // @Type is used with an array of primitives - check if it's a valid wrapper class match
162
174
  const elementType = (0, type_helpers_util_1.getTypeString)(elementTypeNode, checker, esTreeNodeMap);
163
175
  if (elementType) {
164
- context.report({
165
- node,
166
- messageId: 'typeMismatch',
167
- data: {
168
- typeDecoratorClass: typeClassName,
169
- actualType: `${elementType}[]`,
170
- },
171
- });
176
+ const expectedPrimitive = PRIMITIVE_WRAPPER_MAP[typeClassName];
177
+ if (!expectedPrimitive || expectedPrimitive !== elementType) {
178
+ context.report({
179
+ node,
180
+ messageId: 'typeMismatch',
181
+ data: {
182
+ typeDecoratorClass: typeClassName,
183
+ actualType: `${elementType}[]`,
184
+ },
185
+ });
186
+ }
172
187
  }
173
188
  }
174
189
  }
@@ -128,6 +128,20 @@ export declare function isUnionEnumType(typeNode: TSESTree.TypeNode): boolean;
128
128
  * Handles both simple identifiers (MyEnum) and member expressions (MyNamespace.MyEnum).
129
129
  */
130
130
  export declare function getIsEnumArgument(decorator: TSESTree.Decorator): string | null;
131
+ /**
132
+ * Checks if the @IsEnum decorator argument is an array of enum values.
133
+ * This is a valid class-validator pattern for restricting validation to a subset of enum values.
134
+ *
135
+ * Example:
136
+ * const WEIGHT_UNITS = [Unit.G, Unit.KG] as const;
137
+ * @IsEnum(WEIGHT_UNITS)
138
+ * unit?: Unit;
139
+ *
140
+ * Uses TypeScript type checker to determine if the argument resolves to an array/tuple type.
141
+ */
142
+ export declare function isEnumArgumentArraySubset(decorator: TSESTree.Decorator, checker: ts.TypeChecker | null, esTreeNodeMap: {
143
+ get(key: TSESTree.Node): ts.Node | undefined;
144
+ } | null): boolean;
131
145
  /**
132
146
  * Extracts the class name from @Type(() => ClassName) decorator.
133
147
  * Supports both arrow functions and function expressions.
@@ -18,6 +18,7 @@ exports.isArrayType = isArrayType;
18
18
  exports.isTupleType = isTupleType;
19
19
  exports.isUnionEnumType = isUnionEnumType;
20
20
  exports.getIsEnumArgument = getIsEnumArgument;
21
+ exports.isEnumArgumentArraySubset = isEnumArgumentArraySubset;
21
22
  exports.getTypeDecoratorClassName = getTypeDecoratorClassName;
22
23
  exports.hasEachOption = hasEachOption;
23
24
  exports.hasValidateNestedEachOption = hasValidateNestedEachOption;
@@ -670,6 +671,46 @@ function getIsEnumArgument(decorator) {
670
671
  }
671
672
  return null;
672
673
  }
674
+ /**
675
+ * Checks if the @IsEnum decorator argument is an array of enum values.
676
+ * This is a valid class-validator pattern for restricting validation to a subset of enum values.
677
+ *
678
+ * Example:
679
+ * const WEIGHT_UNITS = [Unit.G, Unit.KG] as const;
680
+ * @IsEnum(WEIGHT_UNITS)
681
+ * unit?: Unit;
682
+ *
683
+ * Uses TypeScript type checker to determine if the argument resolves to an array/tuple type.
684
+ */
685
+ function isEnumArgumentArraySubset(decorator, checker, esTreeNodeMap) {
686
+ if (!checker || !esTreeNodeMap) {
687
+ return false;
688
+ }
689
+ if (decorator.expression.type !== 'CallExpression' || decorator.expression.arguments.length === 0) {
690
+ return false;
691
+ }
692
+ const firstArg = decorator.expression.arguments[0];
693
+ // Get the TypeScript node for the argument
694
+ const tsNode = esTreeNodeMap.get(firstArg);
695
+ if (!tsNode) {
696
+ return false;
697
+ }
698
+ try {
699
+ const type = checker.getTypeAtLocation(tsNode);
700
+ // Check if it's an array type
701
+ if (checker.isArrayType(type)) {
702
+ return true;
703
+ }
704
+ // Check if it's a tuple type (for `as const` arrays)
705
+ if (checker.isTupleType(type)) {
706
+ return true;
707
+ }
708
+ return false;
709
+ }
710
+ catch {
711
+ return false;
712
+ }
713
+ }
673
714
  /**
674
715
  * Extracts the class name from @Type(() => ClassName) decorator.
675
716
  * Supports both arrow functions and function expressions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-class-validator-type-match",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "ESLint plugin to ensure class-validator decorators match TypeScript type annotations",
5
5
  "keywords": [
6
6
  "eslint",