1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const utils_1 = require("@typescript-eslint/utils");
|
4 | const util_1 = require("../util");
|
5 | exports.default = (0, util_1.createRule)({
|
6 | name: 'consistent-indexed-object-style',
|
7 | meta: {
|
8 | type: 'suggestion',
|
9 | docs: {
|
10 | description: 'Enforce or disallow the use of the record type',
|
11 |
|
12 | recommended: false,
|
13 | },
|
14 | messages: {
|
15 | preferRecord: 'A record is preferred over an index signature.',
|
16 | preferIndexSignature: 'An index signature is preferred over a record.',
|
17 | },
|
18 | fixable: 'code',
|
19 | schema: [
|
20 | {
|
21 | enum: ['record', 'index-signature'],
|
22 | },
|
23 | ],
|
24 | },
|
25 | defaultOptions: ['record'],
|
26 | create(context, [mode]) {
|
27 | const sourceCode = context.getSourceCode();
|
28 | function checkMembers(members, node, parentId, prefix, postfix, safeFix = true) {
|
29 | if (members.length !== 1) {
|
30 | return;
|
31 | }
|
32 | const [member] = members;
|
33 | if (member.type !== utils_1.AST_NODE_TYPES.TSIndexSignature) {
|
34 | return;
|
35 | }
|
36 | const [parameter] = member.parameters;
|
37 | if (!parameter) {
|
38 | return;
|
39 | }
|
40 | if (parameter.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
41 | return;
|
42 | }
|
43 | const keyType = parameter.typeAnnotation;
|
44 | if (!keyType) {
|
45 | return;
|
46 | }
|
47 | const valueType = member.typeAnnotation;
|
48 | if (!valueType) {
|
49 | return;
|
50 | }
|
51 | if (parentId) {
|
52 | const scope = context.getScope();
|
53 | const superVar = scope.set.get(parentId.name);
|
54 | if (superVar) {
|
55 | const isCircular = superVar.references.some(item => item.isTypeReference &&
|
56 | node.range[0] <= item.identifier.range[0] &&
|
57 | node.range[1] >= item.identifier.range[1]);
|
58 | if (isCircular) {
|
59 | return;
|
60 | }
|
61 | }
|
62 | }
|
63 | context.report({
|
64 | node,
|
65 | messageId: 'preferRecord',
|
66 | fix: safeFix
|
67 | ? (fixer) => {
|
68 | const key = sourceCode.getText(keyType.typeAnnotation);
|
69 | const value = sourceCode.getText(valueType.typeAnnotation);
|
70 | const record = member.readonly
|
71 | ? `Readonly<Record<${key}, ${value}>>`
|
72 | : `Record<${key}, ${value}>`;
|
73 | return fixer.replaceText(node, `${prefix}${record}${postfix}`);
|
74 | }
|
75 | : null,
|
76 | });
|
77 | }
|
78 | return Object.assign(Object.assign({}, (mode === 'index-signature' && {
|
79 | TSTypeReference(node) {
|
80 | var _a;
|
81 | const typeName = node.typeName;
|
82 | if (typeName.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
83 | return;
|
84 | }
|
85 | if (typeName.name !== 'Record') {
|
86 | return;
|
87 | }
|
88 | const params = (_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.params;
|
89 | if ((params === null || params === void 0 ? void 0 : params.length) !== 2) {
|
90 | return;
|
91 | }
|
92 | context.report({
|
93 | node,
|
94 | messageId: 'preferIndexSignature',
|
95 | fix(fixer) {
|
96 | const key = sourceCode.getText(params[0]);
|
97 | const type = sourceCode.getText(params[1]);
|
98 | return fixer.replaceText(node, `{ [key: ${key}]: ${type} }`);
|
99 | },
|
100 | });
|
101 | },
|
102 | })), (mode === 'record' && {
|
103 | TSTypeLiteral(node) {
|
104 | const parent = findParentDeclaration(node);
|
105 | checkMembers(node.members, node, parent === null || parent === void 0 ? void 0 : parent.id, '', '');
|
106 | },
|
107 | TSInterfaceDeclaration(node) {
|
108 | var _a, _b, _c, _d;
|
109 | let genericTypes = '';
|
110 | if (((_b = (_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.params) !== null && _b !== void 0 ? _b : []).length > 0) {
|
111 | genericTypes = `<${(_c = node.typeParameters) === null || _c === void 0 ? void 0 : _c.params.map(p => p.name.name).join(', ')}>`;
|
112 | }
|
113 | checkMembers(node.body.body, node, node.id, `type ${node.id.name}${genericTypes} = `, ';', !((_d = node.extends) === null || _d === void 0 ? void 0 : _d.length));
|
114 | },
|
115 | }));
|
116 | },
|
117 | });
|
118 | function findParentDeclaration(node) {
|
119 | if (node.parent && node.parent.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
120 | if (node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
121 | return node.parent;
|
122 | }
|
123 | return findParentDeclaration(node.parent);
|
124 | }
|
125 | return undefined;
|
126 | }
|
127 |
|
\ | No newline at end of file |