eslint-plugin-nextfriday 1.15.2 → 1.16.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # eslint-plugin-nextfriday
2
2
 
3
+ ## 1.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#75](https://github.com/next-friday/eslint-plugin-nextfriday/pull/75) [`f506561`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/f506561c1587af4a129801dee87c7ed08154b090) Thanks [@joetakara](https://github.com/joetakara)! - feat(rules): add prefer-inline-literal-union, no-nested-interface-declaration, and enforce-type-declaration-order rules
8
+
3
9
  ## 1.15.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -113,7 +113,10 @@ export default [
113
113
  "nextfriday/sort-imports": "error",
114
114
 
115
115
  // Type Patterns
116
+ "nextfriday/enforce-type-declaration-order": "error",
117
+ "nextfriday/no-nested-interface-declaration": "error",
116
118
  "nextfriday/prefer-named-param-types": "error",
119
+ "nextfriday/prefer-inline-literal-union": "error",
117
120
  "nextfriday/prefer-interface-over-inline-types": "error",
118
121
  "nextfriday/sort-type-alphabetically": "error",
119
122
  "nextfriday/sort-type-required-first": "error",
@@ -225,7 +228,10 @@ module.exports = {
225
228
 
226
229
  | Rule | Description | Fixable |
227
230
  | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- |
231
+ | [enforce-type-declaration-order](docs/rules/ENFORCE_TYPE_DECLARATION_ORDER.md) | Enforce referenced types are declared after their consumer | ❌ |
232
+ | [no-nested-interface-declaration](docs/rules/NO_NESTED_INTERFACE_DECLARATION.md) | Disallow inline object types in interface/type properties | ❌ |
228
233
  | [prefer-named-param-types](docs/rules/PREFER_NAMED_PARAM_TYPES.md) | Enforce named types for function parameters with object types | ❌ |
234
+ | [prefer-inline-literal-union](docs/rules/PREFER_INLINE_LITERAL_UNION.md) | Enforce inlining literal union types for better IDE hover info | ✅ |
229
235
  | [prefer-interface-over-inline-types](docs/rules/PREFER_INTERFACE_OVER_INLINE_TYPES.md) | Enforce interface declarations over inline types for React props | ❌ |
230
236
  | [sort-type-alphabetically](docs/rules/SORT_TYPE_ALPHABETICALLY.md) | Enforce A-Z sorting of properties within type groups | ✅ |
231
237
  | [sort-type-required-first](docs/rules/SORT_TYPE_REQUIRED_FIRST.md) | Enforce required properties before optional in types/interfaces | ✅ |
@@ -259,14 +265,14 @@ module.exports = {
259
265
 
260
266
  | Preset | Severity | Base Rules | JSX Rules | Next.js Rules | Total Rules |
261
267
  | -------------------- | -------- | ---------- | --------- | ------------- | ----------- |
262
- | `base` | warn | 33 | 0 | 0 | 33 |
263
- | `base/recommended` | error | 33 | 0 | 0 | 33 |
264
- | `react` | warn | 33 | 14 | 0 | 47 |
265
- | `react/recommended` | error | 33 | 14 | 0 | 47 |
266
- | `nextjs` | warn | 33 | 14 | 1 | 48 |
267
- | `nextjs/recommended` | error | 33 | 14 | 1 | 48 |
268
+ | `base` | warn | 36 | 0 | 0 | 36 |
269
+ | `base/recommended` | error | 36 | 0 | 0 | 36 |
270
+ | `react` | warn | 36 | 14 | 0 | 50 |
271
+ | `react/recommended` | error | 36 | 14 | 0 | 50 |
272
+ | `nextjs` | warn | 36 | 14 | 1 | 51 |
273
+ | `nextjs/recommended` | error | 36 | 14 | 1 | 51 |
268
274
 
269
- ### Base Configuration Rules (33 rules)
275
+ ### Base Configuration Rules (36 rules)
270
276
 
271
277
  Included in `base`, `base/recommended`, and all other presets:
272
278
 
@@ -276,6 +282,7 @@ Included in `base`, `base/recommended`, and all other presets:
276
282
  - `nextfriday/enforce-hook-naming`
277
283
  - `nextfriday/enforce-service-naming`
278
284
  - `nextfriday/enforce-sorted-destructuring`
285
+ - `nextfriday/enforce-type-declaration-order`
279
286
  - `nextfriday/file-kebab-case`
280
287
  - `nextfriday/md-filename-case-restriction`
281
288
  - `nextfriday/newline-after-multiline-block`
@@ -288,6 +295,7 @@ Included in `base`, `base/recommended`, and all other presets:
288
295
  - `nextfriday/no-inline-nested-object`
289
296
  - `nextfriday/no-lazy-identifiers`
290
297
  - `nextfriday/no-logic-in-params`
298
+ - `nextfriday/no-nested-interface-declaration`
291
299
  - `nextfriday/no-nested-ternary`
292
300
  - `nextfriday/no-relative-imports`
293
301
  - `nextfriday/no-single-char-variables`
@@ -296,6 +304,7 @@ Included in `base`, `base/recommended`, and all other presets:
296
304
  - `nextfriday/prefer-function-declaration`
297
305
  - `nextfriday/prefer-guard-clause`
298
306
  - `nextfriday/prefer-import-type`
307
+ - `nextfriday/prefer-inline-literal-union`
299
308
  - `nextfriday/prefer-named-param-types`
300
309
  - `nextfriday/prefer-react-import-types`
301
310
  - `nextfriday/require-explicit-return-type`
@@ -0,0 +1,87 @@
1
+ # enforce-type-declaration-order
2
+
3
+ Enforce that referenced types and interfaces are declared after the type that uses them.
4
+
5
+ ## Rule Details
6
+
7
+ This rule enforces a top-down reading order for type declarations. When an interface or type references another locally-declared type, the referenced (dependency) type must appear _after_ the consumer. This ensures the "main" type is read first, with its supporting types following below.
8
+
9
+ ### Why?
10
+
11
+ 1. **Top-down readability**: The primary type appears first, making it easy to understand the high-level structure before drilling into details
12
+ 2. **Consistency**: A uniform ordering convention eliminates debates about where to place types
13
+ 3. **Natural flow**: Mirrors how you'd explain a data structure - start with the big picture, then define the parts
14
+
15
+ ## Examples
16
+
17
+ ### Incorrect
18
+
19
+ ```ts
20
+ // Dependency declared before consumer
21
+ interface Baz {
22
+ baz: string;
23
+ }
24
+
25
+ interface Foo {
26
+ bar: Baz;
27
+ }
28
+ ```
29
+
30
+ ```ts
31
+ type Config = {
32
+ theme: string;
33
+ };
34
+
35
+ type Props = {
36
+ config: Config;
37
+ };
38
+ ```
39
+
40
+ ### Correct
41
+
42
+ ```ts
43
+ // Consumer first, dependency after
44
+ interface Foo {
45
+ bar: Baz;
46
+ }
47
+
48
+ interface Baz {
49
+ baz: string;
50
+ }
51
+ ```
52
+
53
+ ```ts
54
+ type Props = {
55
+ config: Config;
56
+ };
57
+
58
+ type Config = {
59
+ theme: string;
60
+ };
61
+ ```
62
+
63
+ ```ts
64
+ // Chain of dependencies in top-down order
65
+ interface Parent {
66
+ child: Child;
67
+ }
68
+
69
+ interface Child {
70
+ grandchild: Grandchild;
71
+ }
72
+
73
+ interface Grandchild {
74
+ value: string;
75
+ }
76
+ ```
77
+
78
+ ```ts
79
+ // External/imported types are ignored (not locally declared)
80
+ interface Props {
81
+ items: ExternalType[];
82
+ }
83
+ ```
84
+
85
+ ## When Not To Use It
86
+
87
+ If you prefer declaring dependency types before the types that reference them (bottom-up order), or if you don't want to enforce any particular declaration order, you can disable this rule.
@@ -0,0 +1,102 @@
1
+ # no-nested-interface-declaration
2
+
3
+ Disallow inline object type literals in interface or type properties.
4
+
5
+ ## Rule Details
6
+
7
+ This rule flags inline object type literals nested inside interface or type properties. Nested object types should be extracted into separate named interfaces or type declarations for clarity and reusability.
8
+
9
+ ### Why?
10
+
11
+ 1. **Readability**: Deeply nested inline types make interfaces harder to scan
12
+ 2. **Reusability**: Extracted types can be imported and reused across files
13
+ 3. **Maintainability**: Flat, named types are easier to modify and extend
14
+ 4. **IDE experience**: Named types provide better hover information and navigation
15
+
16
+ ## Examples
17
+
18
+ ### Incorrect
19
+
20
+ ```ts
21
+ interface Foo {
22
+ bar: {
23
+ baz: string;
24
+ };
25
+ }
26
+ ```
27
+
28
+ ```ts
29
+ interface Props {
30
+ user: {
31
+ name: string;
32
+ age: number;
33
+ };
34
+ }
35
+ ```
36
+
37
+ ```ts
38
+ interface Props {
39
+ items: {
40
+ id: number;
41
+ label: string;
42
+ }[];
43
+ }
44
+ ```
45
+
46
+ ```ts
47
+ interface Props {
48
+ data: Readonly<{
49
+ id: number;
50
+ name: string;
51
+ }>;
52
+ }
53
+ ```
54
+
55
+ ### Correct
56
+
57
+ ```ts
58
+ interface Bar {
59
+ baz: string;
60
+ }
61
+
62
+ interface Foo {
63
+ bar: Bar;
64
+ }
65
+ ```
66
+
67
+ ```ts
68
+ interface User {
69
+ name: string;
70
+ age: number;
71
+ }
72
+
73
+ interface Props {
74
+ user: User;
75
+ }
76
+ ```
77
+
78
+ ```ts
79
+ interface Item {
80
+ id: number;
81
+ label: string;
82
+ }
83
+
84
+ interface Props {
85
+ items: Item[];
86
+ }
87
+ ```
88
+
89
+ ```ts
90
+ // Primitive and simple types are fine inline
91
+ interface Props {
92
+ name: string;
93
+ age: number;
94
+ isActive: boolean;
95
+ ids: string[];
96
+ callback: () => void;
97
+ }
98
+ ```
99
+
100
+ ## When Not To Use It
101
+
102
+ If you prefer keeping small object types inline within interfaces for co-location, you can disable this rule.
@@ -0,0 +1,87 @@
1
+ # prefer-inline-literal-union
2
+
3
+ Enforce inlining literal union types in interface properties instead of using type aliases for better IDE hover information.
4
+
5
+ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6
+
7
+ ## Rule Details
8
+
9
+ This rule detects type aliases that are unions of only literal values (strings, numbers, booleans, null, undefined) and flags their usage in interface or type properties. The fix inlines the union directly into the property type annotation.
10
+
11
+ ### Why?
12
+
13
+ When you hover over a prop in your IDE, a type alias shows the alias name (e.g., `ArticleFaqCategoryId`) instead of the actual values (`"articles" | "dharma" | "faq"`). Inlining literal unions gives immediate visibility into the allowed values without needing to jump to the type definition.
14
+
15
+ ## Examples
16
+
17
+ ### Incorrect
18
+
19
+ ```ts
20
+ type ArticleFaqCategoryId = "articles" | "dharma" | "faq";
21
+
22
+ interface ArticleFaqCategoryFilterProps {
23
+ activeCategoryId?: ArticleFaqCategoryId;
24
+ }
25
+ ```
26
+
27
+ ```ts
28
+ type Status = "loading" | "success" | "error";
29
+
30
+ interface Props {
31
+ status: Status;
32
+ }
33
+ ```
34
+
35
+ ```ts
36
+ type Size = 1 | 2 | 3 | 4;
37
+
38
+ interface Props {
39
+ size: Size;
40
+ }
41
+ ```
42
+
43
+ ### Correct
44
+
45
+ ```ts
46
+ interface ArticleFaqCategoryFilterProps {
47
+ activeCategoryId?: "articles" | "dharma" | "faq";
48
+ }
49
+ ```
50
+
51
+ ```ts
52
+ interface Props {
53
+ status: "loading" | "success" | "error";
54
+ }
55
+ ```
56
+
57
+ ```ts
58
+ interface Props {
59
+ size: 1 | 2 | 3 | 4;
60
+ }
61
+ ```
62
+
63
+ ```ts
64
+ // Non-literal unions are allowed as type aliases
65
+ type User = { name: string; age: number };
66
+
67
+ interface Props {
68
+ user: User;
69
+ }
70
+ ```
71
+
72
+ ```ts
73
+ // Using the alias outside of interface properties is fine
74
+ type Status = "loading" | "success" | "error";
75
+ function getStatus(s: Status): string {
76
+ return s;
77
+ }
78
+ ```
79
+
80
+ ## When Not To Use It
81
+
82
+ If you prefer using type aliases for literal unions (e.g., for reuse across multiple interfaces or for self-documenting code), you can disable this rule.
83
+
84
+ ## Further Reading
85
+
86
+ - [TypeScript Union Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types)
87
+ - [TypeScript Literal Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types)