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

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.
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ declare const _default: {
8
8
  }], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
9
9
  'validate-nested-match': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"nestedArrayMismatch" | "missingValidateNested" | "missingEachOption" | "unnecessaryValidateNested" | "tupleValidationWarning" | "multiTypeUnionWarning" | "mixedComplexityUnionWarning" | "pickOmitWarning", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
10
10
  'type-decorator-match': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"typeMismatch" | "missingTypeDecorator", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
11
- 'definite-assignment-match': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"missingDefiniteAssignment", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
11
+ 'definite-assignment-match': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"incorrectDefiniteAssignment", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
12
12
  'dto-filename-match': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"incorrectDtoClassName", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
13
13
  };
14
14
  configs: {
@@ -1,24 +1,29 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
2
  /**
3
- * ESLint rule to ensure properties with decorators use definite assignment assertion (!) when required.
3
+ * ESLint rule to ensure definite assignment assertion (!) is used correctly with decorators.
4
4
  *
5
5
  * This rule validates that:
6
- * - Properties with decorators that are not optional (no ?)
7
- * - Don't have an initializer (no = value)
8
- * - Don't have undefined in their type
9
- * - Use the definite assignment assertion (!)
10
- *
11
- * Note: Properties with | null still require ! unless they also have ?
6
+ * - Properties with ! should NOT have:
7
+ * - Optional marker (?)
8
+ * - An initializer (= value)
9
+ * - undefined in their type
12
10
  *
13
11
  * @example
14
- * // ✅ Good - has definite assignment assertion
12
+ * // ✅ Good - ! without optional or initializer
15
13
  * class User {
16
14
  * @IsString()
17
15
  * name!: string;
18
16
  * }
19
17
  *
20
18
  * @example
21
- * // ✅ Good - optional property doesn't need !
19
+ * // ✅ Good - no ! is also fine for non-optional
20
+ * class User {
21
+ * @IsString()
22
+ * name: string;
23
+ * }
24
+ *
25
+ * @example
26
+ * // ✅ Good - optional property without !
22
27
  * class User {
23
28
  * @IsOptional()
24
29
  * @IsString()
@@ -26,32 +31,40 @@ import { ESLintUtils } from '@typescript-eslint/utils';
26
31
  * }
27
32
  *
28
33
  * @example
29
- * // ✅ Good - has initializer
34
+ * // ✅ Good - has initializer without !
30
35
  * class User {
31
36
  * @IsString()
32
37
  * name: string = 'default';
33
38
  * }
34
39
  *
35
40
  * @example
36
- * // ✅ Good - has undefined in type
41
+ * // ✅ Good - has undefined in type without !
37
42
  * class User {
38
43
  * @IsString()
39
44
  * name: string | undefined;
40
45
  * }
41
46
  *
42
47
  * @example
43
- * // ❌ Bad - missing definite assignment assertion
48
+ * // ❌ Bad - ! with optional property
44
49
  * class User {
50
+ * @IsOptional()
45
51
  * @IsString()
46
- * name: string;
52
+ * name?!: string;
53
+ * }
54
+ *
55
+ * @example
56
+ * // ❌ Bad - ! with initializer
57
+ * class User {
58
+ * @IsString()
59
+ * name!: string = 'default';
47
60
  * }
48
61
  *
49
62
  * @example
50
- * // ❌ Bad - null doesn't count, still needs !
63
+ * // ❌ Bad - ! with undefined in type
51
64
  * class User {
52
65
  * @IsString()
53
- * name: string | null;
66
+ * name!: string | undefined;
54
67
  * }
55
68
  */
56
- declare const _default: ESLintUtils.RuleModule<"missingDefiniteAssignment", [], unknown, ESLintUtils.RuleListener>;
69
+ declare const _default: ESLintUtils.RuleModule<"incorrectDefiniteAssignment", [], unknown, ESLintUtils.RuleListener>;
57
70
  export default _default;
@@ -7,25 +7,30 @@ const type_helpers_util_1 = require("../utils/type-helpers.util");
7
7
  */
8
8
  const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com/robertlinde/eslint-plugin-class-validator-type-match#${name}`);
9
9
  /**
10
- * ESLint rule to ensure properties with decorators use definite assignment assertion (!) when required.
10
+ * ESLint rule to ensure definite assignment assertion (!) is used correctly with decorators.
11
11
  *
12
12
  * This rule validates that:
13
- * - Properties with decorators that are not optional (no ?)
14
- * - Don't have an initializer (no = value)
15
- * - Don't have undefined in their type
16
- * - Use the definite assignment assertion (!)
17
- *
18
- * Note: Properties with | null still require ! unless they also have ?
13
+ * - Properties with ! should NOT have:
14
+ * - Optional marker (?)
15
+ * - An initializer (= value)
16
+ * - undefined in their type
19
17
  *
20
18
  * @example
21
- * // ✅ Good - has definite assignment assertion
19
+ * // ✅ Good - ! without optional or initializer
22
20
  * class User {
23
21
  * @IsString()
24
22
  * name!: string;
25
23
  * }
26
24
  *
27
25
  * @example
28
- * // ✅ Good - optional property doesn't need !
26
+ * // ✅ Good - no ! is also fine for non-optional
27
+ * class User {
28
+ * @IsString()
29
+ * name: string;
30
+ * }
31
+ *
32
+ * @example
33
+ * // ✅ Good - optional property without !
29
34
  * class User {
30
35
  * @IsOptional()
31
36
  * @IsString()
@@ -33,31 +38,39 @@ const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com
33
38
  * }
34
39
  *
35
40
  * @example
36
- * // ✅ Good - has initializer
41
+ * // ✅ Good - has initializer without !
37
42
  * class User {
38
43
  * @IsString()
39
44
  * name: string = 'default';
40
45
  * }
41
46
  *
42
47
  * @example
43
- * // ✅ Good - has undefined in type
48
+ * // ✅ Good - has undefined in type without !
44
49
  * class User {
45
50
  * @IsString()
46
51
  * name: string | undefined;
47
52
  * }
48
53
  *
49
54
  * @example
50
- * // ❌ Bad - missing definite assignment assertion
55
+ * // ❌ Bad - ! with optional property
51
56
  * class User {
57
+ * @IsOptional()
52
58
  * @IsString()
53
- * name: string;
59
+ * name?!: string;
60
+ * }
61
+ *
62
+ * @example
63
+ * // ❌ Bad - ! with initializer
64
+ * class User {
65
+ * @IsString()
66
+ * name!: string = 'default';
54
67
  * }
55
68
  *
56
69
  * @example
57
- * // ❌ Bad - null doesn't count, still needs !
70
+ * // ❌ Bad - ! with undefined in type
58
71
  * class User {
59
72
  * @IsString()
60
- * name: string | null;
73
+ * name!: string | undefined;
61
74
  * }
62
75
  */
63
76
  exports.default = createRule({
@@ -65,10 +78,10 @@ exports.default = createRule({
65
78
  meta: {
66
79
  type: 'problem',
67
80
  docs: {
68
- description: 'Ensure properties with decorators use definite assignment assertion (!) when they are not optional, not initialized, and do not have undefined in their type',
81
+ description: 'Ensure definite assignment assertion (!) is not used with optional properties, initializers, or undefined types',
69
82
  },
70
83
  messages: {
71
- missingDefiniteAssignment: 'Property {{propertyName}} with type {{propertyType}} requires definite assignment assertion (!). Change to: {{propertyName}}!: {{propertyType}}',
84
+ incorrectDefiniteAssignment: 'Property {{propertyName}} should not use definite assignment assertion (!) because it {{reason}}. Remove the ! from: {{propertyName}}!',
72
85
  },
73
86
  schema: [],
74
87
  fixable: 'code',
@@ -105,37 +118,46 @@ exports.default = createRule({
105
118
  if (!actualType)
106
119
  return;
107
120
  /**
108
- * Check for missing definite assignment assertion (!)
109
- * Properties with decorators that are:
110
- * - Not optional (no ?)
111
- * - Not initialized (no = value)
112
- * - Don't have undefined in their type
113
- * Should use the definite assignment assertion (!)
114
- * Note: Properties with | null still require ! unless they also have ?
121
+ * Check for incorrect usage of definite assignment assertion (!)
122
+ * Properties should NOT use ! if they:
123
+ * - Are optional (have ?)
124
+ * - Have an initializer (= value)
125
+ * - Have undefined in their type
115
126
  */
116
127
  const isOptionalProperty = node.optional === true;
117
128
  const hasInitializer = node.value !== undefined && node.value !== null;
118
129
  const hasDefiniteAssignment = node.definite === true;
119
130
  const hasUndefinedInType = typeAnnotation.type === 'TSUndefinedKeyword' ||
120
131
  (typeAnnotation.type === 'TSUnionType' && typeAnnotation.types.some((t) => t.type === 'TSUndefinedKeyword'));
121
- // If property is not optional, not initialized, doesn't have undefined in type,
122
- // and doesn't have definite assignment, report an error
123
- if (!isOptionalProperty && !hasInitializer && !hasUndefinedInType && !hasDefiniteAssignment) {
132
+ // If property has definite assignment (!), check if it's used incorrectly
133
+ if (hasDefiniteAssignment) {
124
134
  const propertyName = node.key.type === 'Identifier' ? node.key.name : 'property';
125
- context.report({
126
- node,
127
- messageId: 'missingDefiniteAssignment',
128
- data: {
129
- propertyName,
130
- propertyType: actualType,
131
- },
132
- fix(fixer) {
133
- // Find position after property key (and after optional marker if present)
134
- // We need to insert ! before the : type annotation
135
- const keyEnd = node.key.range[1];
136
- return fixer.insertTextAfterRange([keyEnd, keyEnd], '!');
137
- },
138
- });
135
+ let reason = null;
136
+ if (isOptionalProperty) {
137
+ reason = 'is optional (has ?)';
138
+ }
139
+ else if (hasInitializer) {
140
+ reason = 'has an initializer';
141
+ }
142
+ else if (hasUndefinedInType) {
143
+ reason = 'has undefined in its type';
144
+ }
145
+ if (reason) {
146
+ context.report({
147
+ node,
148
+ messageId: 'incorrectDefiniteAssignment',
149
+ data: {
150
+ propertyName,
151
+ reason,
152
+ },
153
+ fix(fixer) {
154
+ // Remove the ! after the property key
155
+ const keyEnd = node.key.range[1];
156
+ // The ! is between the key and the optional marker or colon
157
+ return fixer.removeRange([keyEnd, keyEnd + 1]);
158
+ },
159
+ });
160
+ }
139
161
  }
140
162
  },
141
163
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-class-validator-type-match",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "ESLint plugin to ensure class-validator decorators match TypeScript type annotations",
5
5
  "keywords": [
6
6
  "eslint",
package/readme.md CHANGED
@@ -148,7 +148,7 @@ class User {
148
148
 
149
149
  ### `definite-assignment-match`
150
150
 
151
- Ensures properties with decorators use definite assignment assertion (`!`) when required.
151
+ Ensures definite assignment assertion (`!`) is used correctly with decorators.
152
152
 
153
153
  **Examples:**
154
154
 
@@ -157,20 +157,24 @@ import {IsString, IsOptional} from 'class-validator';
157
157
 
158
158
  class User {
159
159
  @IsString()
160
- name!: string; // ✅ Correct - has definite assignment
160
+ name!: string; // ✅ Correct - ! can be used for non-optional
161
161
 
162
162
  @IsString()
163
- email: string; // Error: Missing definite assignment assertion (!)
163
+ email: string; // Correct - ! is optional for non-optional properties
164
164
 
165
165
  @IsOptional()
166
166
  @IsString()
167
- bio?: string; // ✅ Correct - optional property doesn't need !
167
+ bio?: string; // ✅ Correct - optional property without !
168
+
169
+ @IsOptional()
170
+ @IsString()
171
+ username?!: string; // ❌ Error: ! should not be used with optional (?)
168
172
 
169
173
  @IsString()
170
- nickname: string = 'default'; // Correct - has initializer
174
+ nickname!: string = 'default'; // Error: ! should not be used with initializer
171
175
 
172
176
  @IsString()
173
- description: string | undefined; // Correct - has undefined in type
177
+ description!: string | undefined; // Error: ! should not be used with undefined type
174
178
  }
175
179
  ```
176
180