eslint-plugin-no-unused-type-properties 0.1.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/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # eslint-plugin-no-unused-type-properties
2
+
3
+ Standalone ESLint plugin providing the `no-unused-type-properties` rule.
4
+
5
+ ## Usage (flat config)
6
+
7
+ ```ts
8
+ import pluginNoUnusedTypeProperties from 'eslint-plugin-no-unused-type-properties'
9
+
10
+ export default [
11
+ {
12
+ plugins: {
13
+ random: pluginNoUnusedTypeProperties,
14
+ },
15
+ rules: {
16
+ 'random/no-unused-type-properties': 'error',
17
+ },
18
+ },
19
+ ]
20
+ ```
21
+ # eslint-plugin-no-unused-type-properties
@@ -0,0 +1,14 @@
1
+ export declare const rules: {
2
+ 'no-unused-type-properties': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unusedProperties", [], import("./types").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
3
+ name: string;
4
+ };
5
+ };
6
+ declare const plugin: {
7
+ rules: {
8
+ 'no-unused-type-properties': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unusedProperties", [], import("./types").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
9
+ name: string;
10
+ };
11
+ };
12
+ };
13
+ export default plugin;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK;;;;CAEjB,CAAA;AAED,QAAA,MAAM,MAAM;;;;;;CAEX,CAAA;AAED,eAAe,MAAM,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { noUnusedTypePropertiesRule } from './rules/no-unused-type-properties';
2
+ export const rules = {
3
+ 'no-unused-type-properties': noUnusedTypePropertiesRule,
4
+ };
5
+ const plugin = {
6
+ rules,
7
+ };
8
+ export default plugin;
@@ -0,0 +1,5 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const noUnusedTypePropertiesRule: TSESLint.RuleModule<"unusedProperties", [], import("../types").ESLintPluginDocs, TSESLint.RuleListener> & {
3
+ name: string;
4
+ };
5
+ //# sourceMappingURL=no-unused-type-properties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unused-type-properties.d.ts","sourceRoot":"","sources":["../../src/rules/no-unused-type-properties.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AA+DxD,eAAO,MAAM,0BAA0B;;CAgIrC,CAAA"}
@@ -0,0 +1,129 @@
1
+ /* eslint-disable unicorn/no-array-callback-reference */
2
+ import { AST_NODE_TYPES } from '@typescript-eslint/types';
3
+ import { createRule } from '../utils/create-rule';
4
+ function isProperty(property) {
5
+ return property.type === AST_NODE_TYPES.Property;
6
+ }
7
+ function isObjectPattern(param) {
8
+ return param.type === AST_NODE_TYPES.ObjectPattern;
9
+ }
10
+ function isTypeAliasDeclaration(item) {
11
+ return item.type === AST_NODE_TYPES.TSTypeAliasDeclaration;
12
+ }
13
+ function getTypeAliasDeclaration(item) {
14
+ if (isTypeAliasDeclaration(item)) {
15
+ return item;
16
+ }
17
+ if (item.type === AST_NODE_TYPES.ExportNamedDeclaration &&
18
+ item.declaration &&
19
+ item.declaration.type === AST_NODE_TYPES.TSTypeAliasDeclaration) {
20
+ return item.declaration;
21
+ }
22
+ return undefined;
23
+ }
24
+ function isInterfaceDeclaration(item) {
25
+ return item.type === AST_NODE_TYPES.TSInterfaceDeclaration;
26
+ }
27
+ function getInterfaceDeclaration(item) {
28
+ if (isInterfaceDeclaration(item)) {
29
+ return item;
30
+ }
31
+ if (item.type === AST_NODE_TYPES.ExportNamedDeclaration &&
32
+ item.declaration &&
33
+ item.declaration.type === AST_NODE_TYPES.TSInterfaceDeclaration) {
34
+ return item.declaration;
35
+ }
36
+ return undefined;
37
+ }
38
+ export const noUnusedTypePropertiesRule = createRule({
39
+ name: 'no-unused-type-properties',
40
+ meta: {
41
+ type: 'problem',
42
+ docs: {
43
+ description: 'Disallows unused type properties for destructured function arguments',
44
+ recommended: false,
45
+ requiresTypeChecking: false,
46
+ },
47
+ messages: {
48
+ unusedProperties: "Property '{{propertyName}}' is defined in type '{{typeName}}' but is not used in the destructuring. Remove it or use Omit<{{typeName}}, '{{propertyName}}'> to explicitly exclude it.",
49
+ },
50
+ schema: [],
51
+ },
52
+ defaultOptions: [],
53
+ create(context) {
54
+ const checkIfPropertyIsPresent = (objectPattern, typeName) => (typeProperty) => {
55
+ if (typeProperty.type !== AST_NODE_TYPES.TSPropertySignature) {
56
+ return;
57
+ }
58
+ if (typeProperty.key.type !== AST_NODE_TYPES.Identifier) {
59
+ return;
60
+ }
61
+ const propertyName = typeProperty.key.name;
62
+ const properties = objectPattern.properties.filter(isProperty);
63
+ const property = properties.find(property => property.key.type === AST_NODE_TYPES.Identifier && property.key.name === propertyName);
64
+ if (!property) {
65
+ context.report({
66
+ node: objectPattern,
67
+ messageId: 'unusedProperties',
68
+ data: {
69
+ propertyName,
70
+ typeName,
71
+ },
72
+ });
73
+ return;
74
+ }
75
+ if (typeProperty.typeAnnotation && property.value.type === AST_NODE_TYPES.ObjectPattern) {
76
+ recursiveCheck(property.value, typeProperty.typeAnnotation);
77
+ }
78
+ };
79
+ const recursiveCheck = (object, type) => {
80
+ const restElement = object.properties.find(property => property.type === AST_NODE_TYPES.RestElement);
81
+ if (restElement) {
82
+ return;
83
+ }
84
+ if (type.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&
85
+ type.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier) {
86
+ const typeName = type.typeAnnotation.typeName.name;
87
+ const typeDeclaration = context.sourceCode.ast.body
88
+ .map(getTypeAliasDeclaration)
89
+ .find((decl) => decl !== undefined && decl.id.name === typeName);
90
+ if (typeDeclaration) {
91
+ if (typeDeclaration.typeAnnotation.type !== AST_NODE_TYPES.TSTypeLiteral) {
92
+ return;
93
+ }
94
+ typeDeclaration.typeAnnotation.members.forEach(checkIfPropertyIsPresent(object, typeName));
95
+ return;
96
+ }
97
+ const interfaceDeclaration = context.sourceCode.ast.body
98
+ .map(getInterfaceDeclaration)
99
+ .find((decl) => decl !== undefined && decl.id.name === typeName);
100
+ if (!interfaceDeclaration) {
101
+ return;
102
+ }
103
+ interfaceDeclaration.body.body.forEach(checkIfPropertyIsPresent(object, typeName));
104
+ return;
105
+ }
106
+ if (type.typeAnnotation.type === AST_NODE_TYPES.TSTypeLiteral) {
107
+ const inlineTypeName = context.sourceCode.getText(type.typeAnnotation);
108
+ type.typeAnnotation.members.forEach(checkIfPropertyIsPresent(object, inlineTypeName));
109
+ }
110
+ };
111
+ const checkParameter = (paramObjectPattern) => {
112
+ if (!paramObjectPattern.typeAnnotation) {
113
+ return;
114
+ }
115
+ recursiveCheck(paramObjectPattern, paramObjectPattern.typeAnnotation);
116
+ };
117
+ return {
118
+ FunctionDeclaration(node) {
119
+ node.params.filter(isObjectPattern).forEach(checkParameter);
120
+ },
121
+ ArrowFunctionExpression(node) {
122
+ node.params.filter(isObjectPattern).forEach(checkParameter);
123
+ },
124
+ FunctionExpression(node) {
125
+ node.params.filter(isObjectPattern).forEach(checkParameter);
126
+ },
127
+ };
128
+ },
129
+ });
@@ -0,0 +1,6 @@
1
+ export interface ESLintPluginDocs {
2
+ description: string;
3
+ recommended: boolean;
4
+ requiresTypeChecking: boolean;
5
+ }
6
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,OAAO,CAAA;IACpB,oBAAoB,EAAE,OAAO,CAAA;CAC9B"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import type { ESLintPluginDocs } from '../types';
3
+ export declare const createRule: <Options extends readonly unknown[], MessageIds extends string>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, ESLintPluginDocs>>) => ESLintUtils.RuleModule<MessageIds, Options, ESLintPluginDocs, ESLintUtils.RuleListener> & {
4
+ name: string;
5
+ };
6
+ //# sourceMappingURL=create-rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-rule.d.ts","sourceRoot":"","sources":["../../src/utils/create-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEhD,eAAO,MAAM,UAAU;;CAEtB,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export const createRule = ESLintUtils.RuleCreator(name => `https://typescript-eslint.io/rules/${name}`);
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "eslint-plugin-no-unused-type-properties",
3
+ "version": "0.1.0",
4
+ "description": "ESLint plugin containing no-unused-type-properties rule",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "clean": "rm -rf dist",
21
+ "typecheck": "tsc -p tsconfig.json --noEmit"
22
+ },
23
+ "peerDependencies": {
24
+ "@typescript-eslint/types": "^8.57.1",
25
+ "@typescript-eslint/utils": "^8.57.1",
26
+ "eslint": "^9.0.0 || ^10.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@typescript-eslint/types": "^8.57.1",
30
+ "@typescript-eslint/utils": "^8.57.1",
31
+ "eslint": "^10.0.3",
32
+ "typescript": "^5.9.3"
33
+ }
34
+ }