|
9 | 9 | } from "@typescript-eslint/utils/ts-eslint";
|
10 | 10 | import { deepmerge } from "deepmerge-ts";
|
11 | 11 | import { Immutability } from "is-immutable-type";
|
| 12 | +import type { Type, TypeNode } from "typescript"; |
12 | 13 |
|
13 | 14 | import {
|
14 | 15 | type IgnoreClassesOption,
|
| 16 | + type OverridableOptions, |
| 17 | + type RawOverridableOptions, |
| 18 | + getCoreOptions, |
| 19 | + getCoreOptionsForType, |
15 | 20 | ignoreClassesOptionSchema,
|
16 | 21 | shouldIgnoreClasses,
|
17 | 22 | shouldIgnoreInFunction,
|
18 | 23 | shouldIgnorePattern,
|
| 24 | + upgradeRawOverridableOptions, |
19 | 25 | } from "#/options";
|
20 | 26 | import { ruleNameScope } from "#/utils/misc";
|
21 | 27 | import type { ESFunctionType } from "#/utils/node-types";
|
|
25 | 31 | type RuleResult,
|
26 | 32 | createRule,
|
27 | 33 | getReturnTypesOfFunction,
|
| 34 | + getTypeDataOfNode, |
28 | 35 | getTypeImmutabilityOfNode,
|
29 | 36 | getTypeImmutabilityOfType,
|
30 | 37 | isImplementationOfOverload,
|
|
43 | 50 | isTSTypePredicate,
|
44 | 51 | } from "#/utils/type-guards";
|
45 | 52 |
|
| 53 | +import { overridableOptionsSchema } from "../utils/schemas"; |
| 54 | + |
46 | 55 | /**
|
47 | 56 | * The name of this rule.
|
48 | 57 | */
|
|
56 | 65 | type RawEnforcement =
|
57 | 66 | | Exclude<Immutability | keyof typeof Immutability, "Unknown" | "Mutable">
|
58 | 67 | | "None"
|
59 |
| - | false; |
| 68 | + | false |
| 69 | + | undefined; |
60 | 70 |
|
61 | 71 | type Option = IgnoreClassesOption & {
|
62 | 72 | enforcement: RawEnforcement;
|
|
65 | 75 | ignoreTypePattern?: string[] | string;
|
66 | 76 | };
|
67 | 77 |
|
| 78 | +type CoreOptions = Option & { |
| 79 | + parameters?: Partial<Option> | RawEnforcement; |
| 80 | + returnTypes?: Partial<Option> | RawEnforcement; |
| 81 | + variables?: |
| 82 | + | Partial< |
| 83 | + Option & { |
| 84 | + ignoreInFunctions?: boolean; |
| 85 | + } |
| 86 | + > |
| 87 | + | RawEnforcement; |
| 88 | + fixer?: FixerConfigRawMap; |
| 89 | + suggestions?: SuggestionConfigRawMap; |
| 90 | +}; |
| 91 | + |
68 | 92 | type FixerConfigRaw = {
|
69 | 93 | pattern: string;
|
70 | 94 | replace: string;
|
|
96 | 120 | /**
|
97 | 121 | * The options this rule can take.
|
98 | 122 | */
|
99 |
| -type Options = [ |
100 |
| - Option & { |
101 |
| - parameters?: Partial<Option> | RawEnforcement; |
102 |
| - returnTypes?: Partial<Option> | RawEnforcement; |
103 |
| - variables?: |
104 |
| - | Partial< |
105 |
| - Option & { |
106 |
| - ignoreInFunctions?: boolean; |
107 |
| - } |
108 |
| - > |
109 |
| - | RawEnforcement; |
110 |
| - fixer?: FixerConfigRawMap; |
111 |
| - suggestions?: SuggestionConfigRawMap; |
112 |
| - }, |
113 |
| -]; |
| 123 | +type RawOptions = [RawOverridableOptions<CoreOptions>]; |
| 124 | +type Options = OverridableOptions<CoreOptions>; |
114 | 125 |
|
115 | 126 | /**
|
116 | 127 | * The enum options for the level of enforcement.
|
|
215 | 226 | },
|
216 | 227 | };
|
217 | 228 |
|
218 |
| -/** |
219 |
| - * The schema for the rule options. |
220 |
| - */ |
221 |
| -const schema: JSONSchema4[] = [ |
222 |
| - { |
223 |
| - type: "object", |
224 |
| - properties: deepmerge(optionExpandedSchema, { |
225 |
| - parameters: optionSchema, |
226 |
| - returnTypes: optionSchema, |
227 |
| - variables: { |
228 |
| - oneOf: [ |
229 |
| - { |
230 |
| - type: "object", |
231 |
| - properties: deepmerge(optionExpandedSchema, { |
232 |
| - ignoreInFunctions: { |
233 |
| - type: "boolean", |
234 |
| - }, |
235 |
| - } satisfies JSONSchema4ObjectSchema["properties"]), |
236 |
| - additionalProperties: false, |
237 |
| - }, |
238 |
| - { |
239 |
| - type: ["string", "number", "boolean"], |
240 |
| - enum: enforcementEnumOptions, |
241 |
| - }, |
242 |
| - ], |
243 |
| - }, |
244 |
| - fixer: { |
| 229 | +const coreOptionsPropertiesSchema: NonNullable< |
| 230 | + JSONSchema4ObjectSchema["properties"] |
| 231 | +> = deepmerge(optionExpandedSchema, { |
| 232 | + parameters: optionSchema, |
| 233 | + returnTypes: optionSchema, |
| 234 | + variables: { |
| 235 | + oneOf: [ |
| 236 | + { |
245 | 237 | type: "object",
|
246 |
| - properties: { |
247 |
| - ReadonlyShallow: fixerSchema, |
248 |
| - ReadonlyDeep: fixerSchema, |
249 |
| - Immutable: fixerSchema, |
250 |
| - }, |
| 238 | + properties: deepmerge(optionExpandedSchema, { |
| 239 | + ignoreInFunctions: { |
| 240 | + type: "boolean", |
| 241 | + }, |
| 242 | + } satisfies JSONSchema4ObjectSchema["properties"]), |
251 | 243 | additionalProperties: false,
|
252 | 244 | },
|
253 |
| - suggestions: { |
254 |
| - type: "object", |
255 |
| - properties: { |
256 |
| - ReadonlyShallow: suggestionsSchema, |
257 |
| - ReadonlyDeep: suggestionsSchema, |
258 |
| - Immutable: suggestionsSchema, |
259 |
| - }, |
260 |
| - additionalProperties: false, |
| 245 | + { |
| 246 | + type: ["string", "number", "boolean"], |
| 247 | + enum: enforcementEnumOptions, |
261 | 248 | },
|
262 |
| - } satisfies JSONSchema4ObjectSchema["properties"]), |
| 249 | + ], |
| 250 | + }, |
| 251 | + fixer: { |
| 252 | + type: "object", |
| 253 | + properties: { |
| 254 | + ReadonlyShallow: fixerSchema, |
| 255 | + ReadonlyDeep: fixerSchema, |
| 256 | + Immutable: fixerSchema, |
| 257 | + }, |
263 | 258 | additionalProperties: false,
|
264 | 259 | },
|
| 260 | + suggestions: { |
| 261 | + type: "object", |
| 262 | + properties: { |
| 263 | + ReadonlyShallow: suggestionsSchema, |
| 264 | + ReadonlyDeep: suggestionsSchema, |
| 265 | + Immutable: suggestionsSchema, |
| 266 | + }, |
| 267 | + additionalProperties: false, |
| 268 | + }, |
| 269 | +} satisfies JSONSchema4ObjectSchema["properties"]); |
| 270 | + |
| 271 | +/** |
| 272 | + * The schema for the rule options. |
| 273 | + */ |
| 274 | +const schema: JSONSchema4[] = [ |
| 275 | + overridableOptionsSchema(coreOptionsPropertiesSchema), |
265 | 276 | ];
|
266 | 277 |
|
267 | 278 | /**
|
268 | 279 | * The default options for the rule.
|
269 | 280 | */
|
270 |
| -const defaultOptions: Options = [ |
| 281 | +const defaultOptions: RawOptions = [ |
271 | 282 | {
|
272 | 283 | enforcement: Immutability.Immutable,
|
273 | 284 | ignoreInferredTypes: false,
|
|
337 | 348 |
|
338 | 349 | type Descriptor = RuleResult<
|
339 | 350 | keyof typeof errorMessages,
|
340 |
| - Options |
| 351 | + RawOptions |
341 | 352 | >["descriptors"][number];
|
342 | 353 |
|
343 | 354 | type AllFixers = {
|
|
350 | 361 | */
|
351 | 362 | function getAllFixers(
|
352 | 363 | node: TSESTree.Node,
|
353 |
| - context: Readonly<RuleContext<keyof typeof errorMessages, Options>>, |
| 364 | + context: Readonly<RuleContext<keyof typeof errorMessages, RawOptions>>, |
354 | 365 | fixerConfigs: FixerConfig[] | false,
|
355 | 366 | suggestionsConfigs: SuggestionsConfig[] | false,
|
356 | 367 | ): AllFixers {
|
|
422 | 433 | * Get the level of enforcement from the raw value given.
|
423 | 434 | */
|
424 | 435 | function parseEnforcement(rawEnforcement: RawEnforcement) {
|
425 |
| - return rawEnforcement === "None" |
| 436 | + return rawEnforcement === "None" || rawEnforcement === undefined |
426 | 437 | ? false
|
427 | 438 | : typeof rawEnforcement === "string"
|
428 | 439 | ? Immutability[rawEnforcement]
|
|
433 | 444 | * Get the fixer config for the the given enforcement level from the raw config given.
|
434 | 445 | */
|
435 | 446 | function parseFixerConfigs(
|
436 |
| - allRawConfigs: Options[0]["fixer"], |
| 447 | + allRawConfigs: RawOptions[0]["fixer"], |
437 | 448 | enforcement: Immutability,
|
438 | 449 | ): FixerConfig[] | false {
|
439 | 450 | const key = Immutability[enforcement] as keyof NonNullable<
|
|
454 | 465 | * Get the suggestions config for the the given enforcement level from the raw config given.
|
455 | 466 | */
|
456 | 467 | function parseSuggestionsConfigs(
|
457 |
| - rawSuggestions: Options[0]["suggestions"], |
| 468 | + rawSuggestions: RawOptions[0]["suggestions"], |
458 | 469 | enforcement: Immutability,
|
459 | 470 | ): SuggestionsConfig[] | false {
|
460 | 471 | const key = Immutability[enforcement] as keyof NonNullable<
|
|
477 | 488 | */
|
478 | 489 | function getParameterTypeViolations(
|
479 | 490 | node: ESFunctionType,
|
480 |
| - context: Readonly<RuleContext<keyof typeof errorMessages, Options>>, |
| 491 | + context: Readonly<RuleContext<keyof typeof errorMessages, RawOptions>>, |
481 | 492 | options: Readonly<Options>,
|
482 | 493 | ): Descriptor[] {
|
483 |
| - const [optionsObject] = options; |
484 |
| - const { |
485 |
| - parameters: rawOption, |
486 |
| - fixer: rawFixerConfig, |
487 |
| - suggestions: rawSuggestionsConfigs, |
488 |
| - } = optionsObject; |
489 |
| - const { |
490 |
| - enforcement: rawEnforcement, |
491 |
| - ignoreInferredTypes, |
492 |
| - ignoreClasses, |
493 |
| - ignoreNamePattern, |
494 |
| - ignoreTypePattern, |
495 |
| - } = { |
496 |
| - ignoreInferredTypes: optionsObject.ignoreInferredTypes, |
497 |
| - ignoreClasses: optionsObject.ignoreClasses, |
498 |
| - ignoreNamePattern: optionsObject.ignoreNamePattern, |
499 |
| - ignoreTypePattern: optionsObject.ignoreTypePattern, |
500 |
| - ...(typeof rawOption === "object" |
501 |
| - ? rawOption |
502 |
| - : { |
503 |
| - enforcement: rawOption, |
504 |
| - }), |
505 |
| - }; |
| 494 | + return node.params |
| 495 | + .map((param): Descriptor | undefined => { |
| 496 | + const parameterProperty = isTSParameterProperty(param); |
| 497 | + const actualParam = parameterProperty ? param.parameter : param; |
506 | 498 |
|
507 |
| - const enforcement = parseEnforcement( |
508 |
| - rawEnforcement ?? optionsObject.enforcement, |
509 |
| - ); |
510 |
| - if ( |
511 |
| - enforcement === false || |
512 |
| - shouldIgnoreClasses(node, context, ignoreClasses) |
513 |
| - ) { |
514 |
| - return []; |
515 |
| - } |
| 499 | + const optionsToUse = getCoreOptions<CoreOptions, Options>( |
| 500 | + param, |
| 501 | + context, |
| 502 | + options, |
| 503 | + ); |
516 | 504 |
|
517 |
| - const fixerConfigs = parseFixerConfigs(rawFixerConfig, enforcement); |
518 |
| - const suggestionsConfigs = parseSuggestionsConfigs( |
519 |
| - rawSuggestionsConfigs, |
520 |
| - enforcement, |
521 |
| - ); |
| 505 | + if (optionsToUse === null) { |
| 506 | + return undefined; |
| 507 | + } |
| 508 | + |
| 509 | + const { |
| 510 | + parameters: rawOption, |
| 511 | + fixer: rawFixerConfig, |
| 512 | + suggestions: rawSuggestionsConfigs, |
| 513 | + } = optionsToUse; |
| 514 | + const { |
| 515 | + enforcement: rawEnforcement, |
| 516 | + ignoreInferredTypes, |
| 517 | + ignoreClasses, |
| 518 | + ignoreNamePattern, |
| 519 | + ignoreTypePattern, |
| 520 | + } = { |
| 521 | + ignoreInferredTypes: optionsToUse.ignoreInferredTypes, |
| 522 | + ignoreClasses: optionsToUse.ignoreClasses, |
| 523 | + ignoreNamePattern: optionsToUse.ignoreNamePattern, |
| 524 | + ignoreTypePattern: optionsToUse.ignoreTypePattern, |
| 525 | + ...(typeof rawOption === "object" |
| 526 | + ? rawOption |
| 527 | + : { |
| 528 | + enforcement: rawOption, |
| 529 | + }), |
| 530 | + }; |
| 531 | + |
| 532 | + const enforcement = parseEnforcement( |
| 533 | + rawEnforcement ?? optionsToUse.enforcement, |
| 534 | + ); |
| 535 | + if ( |
| 536 | + enforcement === false || |
| 537 | + shouldIgnoreClasses(node, context, ignoreClasses) |
| 538 | + ) { |
| 539 | + return undefined; |
| 540 | + } |
| 541 | + |
| 542 | + const fixerConfigs = parseFixerConfigs(rawFixerConfig, enforcement); |
| 543 | + const suggestionsConfigs = parseSuggestionsConfigs( |
| 544 | + rawSuggestionsConfigs, |
| 545 | + enforcement, |
| 546 | + ); |
522 | 547 |
|
523 |
| - return node.params |
524 |
| - .map((param): Descriptor | undefined => { |
525 | 548 | if (shouldIgnorePattern(param, context, ignoreNamePattern)) {
|
526 | 549 | return undefined;
|
527 | 550 | }
|
528 | 551 |
|
529 |
| - const parameterProperty = isTSParameterProperty(param); |
530 | 552 | if (parameterProperty && !param.readonly) {
|
531 | 553 | const fix: NonNullable<Descriptor["fix"]> | null = (fixer) =>
|
532 | 554 | fixer.insertTextBefore(param.parameter, "readonly ");
|
|
544 | 566 | };
|
545 | 567 | }
|
546 | 568 |
|
547 |
| - const actualParam = parameterProperty ? param.parameter : param; |
548 |
| - |
549 | 569 | if (
|
550 | 570 | // inferred types
|
551 | 571 | (ignoreInferredTypes && actualParam.typeAnnotation === undefined) ||
|
|
596 | 616 | },
|
597 | 617 | fix,
|
598 | 618 | suggest:
|
599 | 619 | suggestionFixers?.map(({ fix, message }) => ({
|
600 | 620 | messageId: "userDefined",
|
601 | 621 | data: {
|
602 | 622 | message,
|
|
613 | 633 | */
|
614 | 634 | function getReturnTypeViolations(
|
615 | 635 | node: ESFunctionType,
|
616 |
| - context: Readonly<RuleContext<keyof typeof errorMessages, Options>>, |
| 636 | + context: Readonly<RuleContext<keyof typeof errorMessages, RawOptions>>, |
617 | 637 | options: Readonly<Options>,
|
618 | 638 | ): Descriptor[] {
|
619 |
| - const [optionsObject] = options; |
620 |
| - const { |
621 |
| - returnTypes: rawOption, |
622 |
| - fixer: rawFixerConfig, |
623 |
| - suggestions: rawSuggestionsConfigs, |
624 |
| - } = optionsObject; |
625 |
| - const { |
626 |
| - enforcement: rawEnforcement, |
627 |
| - ignoreInferredTypes, |
628 |
| - ignoreClasses, |
629 |
| - ignoreNamePattern, |
630 |
| - ignoreTypePattern, |
631 |
| - } = { |
632 |
| - ignoreInferredTypes: optionsObject.ignoreInferredTypes, |
633 |
| - ignoreClasses: optionsObject.ignoreClasses, |
634 |
| - ignoreNamePattern: optionsObject.ignoreNamePattern, |
635 |
| - ignoreTypePattern: optionsObject.ignoreTypePattern, |
636 |
| - ...(typeof rawOption === "object" ? rawOption : { enforcement: rawOption }), |
637 |
| - }; |
| 639 | + function getOptions(type: Type, typeNode: TypeNode | null) { |
| 640 | + const optionsToUse = getCoreOptionsForType<CoreOptions, Options>( |
| 641 | + type, |
| 642 | + typeNode, |
| 643 | + context, |
| 644 | + options, |
| 645 | + ); |
638 | 646 |
|
639 |
| - const enforcement = parseEnforcement( |
640 |
| - rawEnforcement ?? optionsObject.enforcement, |
641 |
| - ); |
| 647 | + if (optionsToUse === null) { |
| 648 | + return null; |
| 649 | + } |
642 | 650 |
|
643 |
| - if ( |
644 |
| - enforcement === false || |
645 |
| - (ignoreInferredTypes && node.returnType?.typeAnnotation === undefined) || |
646 |
| - shouldIgnoreClasses(node, context, ignoreClasses) || |
647 |
| - shouldIgnorePattern(node, context, ignoreNamePattern) |
648 |
| - ) { |
649 |
| - return []; |
650 |
| - } |
| 651 | + const { |
| 652 | + returnTypes: rawOption, |
| 653 | + fixer: rawFixerConfig, |
| 654 | + suggestions: rawSuggestionsConfigs, |
| 655 | + } = optionsToUse; |
| 656 | + const { |
| 657 | + enforcement: rawEnforcement, |
| 658 | + ignoreClasses, |
| 659 | + ignoreNamePattern, |
| 660 | + ignoreTypePattern, |
| 661 | + ignoreInferredTypes, |
| 662 | + } = { |
| 663 | + ignoreClasses: optionsToUse.ignoreClasses, |
| 664 | + ignoreNamePattern: optionsToUse.ignoreNamePattern, |
| 665 | + ignoreTypePattern: optionsToUse.ignoreTypePattern, |
| 666 | + ignoreInferredTypes: optionsToUse.ignoreInferredTypes, |
| 667 | + ...(typeof rawOption === "object" |
| 668 | + ? rawOption |
| 669 | + : { enforcement: rawOption }), |
| 670 | + }; |
651 | 671 |
|
652 |
| - const fixerConfigs = parseFixerConfigs(rawFixerConfig, enforcement); |
653 |
| - const suggestionsConfigs = parseSuggestionsConfigs( |
654 |
| - rawSuggestionsConfigs, |
655 |
| - enforcement, |
656 |
| - ); |
| 672 | + const enforcement = parseEnforcement( |
| 673 | + rawEnforcement ?? optionsToUse.enforcement, |
| 674 | + ); |
657 | 675 |
|
658 |
| - if ( |
659 |
| - node.returnType?.typeAnnotation !== undefined && |
660 |
| - !isTSTypePredicate(node.returnType.typeAnnotation) |
661 |
| - ) { |
662 |
| - if (shouldIgnorePattern(node.returnType, context, ignoreTypePattern)) { |
663 |
| - return []; |
| 676 | + if ( |
| 677 | + enforcement === false || |
| 678 | + shouldIgnoreClasses(node, context, ignoreClasses) || |
| 679 | + shouldIgnorePattern(node, context, ignoreNamePattern) |
| 680 | + ) { |
| 681 | + return null; |
664 | 682 | }
|
665 | 683 |
|
666 |
| - const immutability = getTypeImmutabilityOfNode( |
667 |
| - node.returnType.typeAnnotation, |
668 |
| - context, |
| 684 | + const fixerConfigs = parseFixerConfigs(rawFixerConfig, enforcement); |
| 685 | + const suggestionsConfigs = parseSuggestionsConfigs( |
| 686 | + rawSuggestionsConfigs, |
669 | 687 | enforcement,
|
670 | 688 | );
|
671 | 689 |
|
672 |
| - if (immutability >= enforcement) { |
| 690 | + return { |
| 691 | + ignoreTypePattern, |
| 692 | + ignoreInferredTypes, |
| 693 | + enforcement, |
| 694 | + fixerConfigs, |
| 695 | + suggestionsConfigs, |
| 696 | + }; |
| 697 | + } |
| 698 | + |
| 699 | + if (node.returnType?.typeAnnotation !== undefined) { |
| 700 | + const [type, typeNode] = getTypeDataOfNode(node, context); |
| 701 | + const optionsToUse = getOptions(type, typeNode); |
| 702 | + if (optionsToUse === null) { |
673 | 703 | return [];
|
674 | 704 | }
|
675 | 705 |
|
676 |
| - const { fix, suggestionFixers } = getAllFixers( |
677 |
| - node.returnType.typeAnnotation, |
678 |
| - context, |
679 |
| - fixerConfigs, |
680 |
| - suggestionsConfigs, |
681 |
| - ); |
| 706 | + const { ignoreTypePattern, enforcement, fixerConfigs, suggestionsConfigs } = |
| 707 | + optionsToUse; |
682 | 708 |
|
683 |
| - return [ |
684 |
| - { |
685 |
| - node: node.returnType, |
686 |
| - messageId: "returnType", |
687 |
| - data: { |
688 |
| - actual: Immutability[immutability], |
689 |
| - expected: Immutability[enforcement], |
| 709 | + if ( |
| 710 | + node.returnType?.typeAnnotation !== undefined && |
| 711 | + !isTSTypePredicate(node.returnType.typeAnnotation) |
| 712 | + ) { |
| 713 | + if (shouldIgnorePattern(node.returnType, context, ignoreTypePattern)) { |
| 714 | + return []; |
| 715 | + } |
| 716 | + |
| 717 | + const immutability = getTypeImmutabilityOfNode( |
| 718 | + node.returnType.typeAnnotation, |
| 719 | + context, |
| 720 | + enforcement, |
| 721 | + ); |
| 722 | + |
| 723 | + if (immutability >= enforcement) { |
| 724 | + return []; |
| 725 | + } |
| 726 | + |
| 727 | + const { fix, suggestionFixers } = getAllFixers( |
| 728 | + node.returnType.typeAnnotation, |
| 729 | + context, |
| 730 | + fixerConfigs, |
| 731 | + suggestionsConfigs, |
| 732 | + ); |
| 733 | + |
| 734 | + return [ |
| 735 | + { |
| 736 | + node: node.returnType, |
| 737 | + messageId: "returnType", |
| 738 | + data: { |
| 739 | + actual: Immutability[immutability], |
| 740 | + expected: Immutability[enforcement], |
| 741 | + }, |
| 742 | + fix, |
| 743 | + suggest: |
| 744 | + suggestionFixers?.map(({ fix, message }) => ({ |
| 745 | + messageId: "userDefined", |
| 746 | + data: { |
| 747 | + message, |
| 748 | + }, |
| 749 | + fix, |
| 750 | + })) ?? null, |
690 | 751 | },
|
691 |
| - fix, |
692 |
| - suggest: |
693 |
| - suggestionFixers?.map(({ fix, message }) => ({ |
694 |
| - messageId: "userDefined", |
695 |
| - data: { |
696 |
| - message, |
697 |
| - }, |
698 |
| - fix, |
699 |
| - })) ?? null, |
700 |
| - }, |
701 |
| - ]; |
| 752 | + ]; |
| 753 | + } |
702 | 754 | }
|
703 | 755 |
|
704 | 756 | if (!isFunctionLike(node)) {
|
|
714 | 766 | return [];
|
715 | 767 | }
|
716 | 768 |
|
| 769 | + const returnType = returnTypes[0]!; |
| 770 | + |
| 771 | + const optionsToUse = getOptions( |
| 772 | + returnType, |
| 773 | + (returnType as Type & { node: TypeNode }).node ?? null, |
| 774 | + ); |
| 775 | + if (optionsToUse === null) { |
| 776 | + return []; |
| 777 | + } |
| 778 | + |
| 779 | + const { ignoreInferredTypes, enforcement, fixerConfigs, suggestionsConfigs } = |
| 780 | + optionsToUse; |
| 781 | + |
| 782 | + if (ignoreInferredTypes) { |
| 783 | + return []; |
| 784 | + } |
| 785 | + |
717 | 786 | const immutability = getTypeImmutabilityOfType(
|
718 |
| - returnTypes[0]!, |
| 787 | + returnType, |
719 | 788 | context,
|
720 | 789 | enforcement,
|
721 | 790 | );
|
|
744 | 813 | },
|
745 | 814 | fix,
|
746 | 815 | suggest:
|
747 | 816 | suggestionFixers?.map(({ fix, message }) => ({
|
748 | 817 | messageId: "userDefined",
|
749 | 818 | data: {
|
750 | 819 | message,
|
|
760 | 829 | */
|
761 | 830 | function checkFunction(
|
762 | 831 | node: ESFunctionType,
|
763 |
| - context: Readonly<RuleContext<keyof typeof errorMessages, Options>>, |
764 |
| - options: Readonly<Options>, |
765 |
| -): RuleResult<keyof typeof errorMessages, Options> { |
| 832 | + context: Readonly<RuleContext<keyof typeof errorMessages, RawOptions>>, |
| 833 | + rawOptions: Readonly<RawOptions>, |
| 834 | +): RuleResult<keyof typeof errorMessages, RawOptions> { |
| 835 | + const options = upgradeRawOverridableOptions(rawOptions[0]); |
| 836 | + |
766 | 837 | const descriptors = [
|
767 | 838 | ...getParameterTypeViolations(node, context, options),
|
768 | 839 | ...getReturnTypeViolations(node, context, options),
|
|
779 | 850 | */
|
780 | 851 | function checkVariable(
|
781 | 852 | node: TSESTree.VariableDeclarator | TSESTree.PropertyDefinition,
|
782 |
| - context: Readonly<RuleContext<keyof typeof errorMessages, Options>>, |
783 |
| - options: Readonly<Options>, |
784 |
| -): RuleResult<keyof typeof errorMessages, Options> { |
785 |
| - const [optionsObject] = options; |
| 853 | + context: Readonly<RuleContext<keyof typeof errorMessages, RawOptions>>, |
| 854 | + rawOptions: Readonly<RawOptions>, |
| 855 | +): RuleResult<keyof typeof errorMessages, RawOptions> { |
| 856 | + const options = upgradeRawOverridableOptions(rawOptions[0]); |
| 857 | + const optionsToUse = getCoreOptions<CoreOptions, Options>( |
| 858 | + node, |
| 859 | + context, |
| 860 | + options, |
| 861 | + ); |
| 862 | + |
| 863 | + if (optionsToUse === null) { |
| 864 | + return { |
| 865 | + context, |
| 866 | + descriptors: [], |
| 867 | + }; |
| 868 | + } |
786 | 869 |
|
787 | 870 | const {
|
788 | 871 | variables: rawOption,
|
789 | 872 | fixer: rawFixerConfig,
|
790 | 873 | suggestions: rawSuggestionsConfigs,
|
791 |
| - } = optionsObject; |
| 874 | + } = optionsToUse; |
792 | 875 | const {
|
793 | 876 | enforcement: rawEnforcement,
|
794 | 877 | ignoreInferredTypes,
|
|
797 | 880 | ignoreTypePattern,
|
798 | 881 | ignoreInFunctions,
|
799 | 882 | } = {
|
800 |
| - ignoreInferredTypes: optionsObject.ignoreInferredTypes, |
801 |
| - ignoreClasses: optionsObject.ignoreClasses, |
802 |
| - ignoreNamePattern: optionsObject.ignoreNamePattern, |
803 |
| - ignoreTypePattern: optionsObject.ignoreTypePattern, |
| 883 | + ignoreInferredTypes: optionsToUse.ignoreInferredTypes, |
| 884 | + ignoreClasses: optionsToUse.ignoreClasses, |
| 885 | + ignoreNamePattern: optionsToUse.ignoreNamePattern, |
| 886 | + ignoreTypePattern: optionsToUse.ignoreTypePattern, |
804 | 887 | ignoreInFunctions: false,
|
805 | 888 | ...(typeof rawOption === "object" ? rawOption : { enforcement: rawOption }),
|
806 | 889 | };
|
807 | 890 |
|
808 | 891 | const enforcement = parseEnforcement(
|
809 |
| - rawEnforcement ?? optionsObject.enforcement, |
| 892 | + rawEnforcement ?? optionsToUse.enforcement, |
810 | 893 | );
|
811 | 894 |
|
812 | 895 | if (
|
|
931 | 1014 | data,
|
932 | 1015 | fix,
|
933 | 1016 | suggest:
|
934 | 1017 | suggestionFixers?.map(({ fix, message }) => ({
|
935 | 1018 | messageId: "userDefined",
|
936 | 1019 | data: {
|
937 | 1020 | message,
|
|
944 | 1027 | }
|
945 | 1028 |
|
946 | 1029 | // Create the rule.
|
947 |
| -export const rule: Rule<keyof typeof errorMessages, Options> = createRule< |
| 1030 | +export const rule: Rule<keyof typeof errorMessages, RawOptions> = createRule< |
948 | 1031 | keyof typeof errorMessages,
|
949 |
| - Options |
| 1032 | + RawOptions |
950 | 1033 | >(name, meta, defaultOptions, {
|
951 | 1034 | ArrowFunctionExpression: checkFunction,
|
952 | 1035 | FunctionDeclaration: checkFunction,
|
|
0 commit comments