From bc0ce5436086d822deb88234392739ebcb9b7ffe Mon Sep 17 00:00:00 2001 From: Alexander T Date: Thu, 27 Jun 2019 16:02:40 +0300 Subject: [PATCH 1/3] feat(eslint-plugin): [array-type] add "default", "readonly" options --- .../eslint-plugin/src/rules/array-type.ts | 88 ++++-- .../tests/rules/array-type.test.ts | 271 ++++++++++++------ 2 files changed, 250 insertions(+), 109 deletions(-) diff --git a/packages/eslint-plugin/src/rules/array-type.ts b/packages/eslint-plugin/src/rules/array-type.ts index 12b425fdd54..ed10abc736c 100644 --- a/packages/eslint-plugin/src/rules/array-type.ts +++ b/packages/eslint-plugin/src/rules/array-type.ts @@ -72,13 +72,21 @@ function typeNeedsParentheses(node: TSESTree.Node): boolean { } } -type Options = ['array' | 'generic' | 'array-simple']; +export type ArrayOption = 'array' | 'generic' | 'array-simple'; +type Options = [ + { + default: ArrayOption; + readonly?: ArrayOption; + } +]; type MessageIds = | 'errorStringGeneric' | 'errorStringGenericSimple' | 'errorStringArray' | 'errorStringArraySimple'; +const arrayOption = { enum: ['array', 'generic', 'array-simple'] }; + export default util.createRule({ name: 'array-type', meta: { @@ -101,14 +109,32 @@ export default util.createRule({ }, schema: [ { - enum: ['array', 'generic', 'array-simple'], + type: 'object', + properties: { + default: arrayOption, + readonly: arrayOption, + }, }, ], }, - defaultOptions: ['array'], - create(context, [option]) { + defaultOptions: [ + { + default: 'array', + }, + ], + create(context, [options]) { const sourceCode = context.getSourceCode(); + const defaultOption = options.default; + const readonlyOption = options.readonly || defaultOption; + + const isArraySimpleOption = + defaultOption === 'array-simple' && readonlyOption === 'array-simple'; + const isArrayOption = + defaultOption === 'array' && readonlyOption === 'array'; + const isGenericOption = + defaultOption === 'generic' && readonlyOption === 'generic'; + /** * Check if whitespace is needed before this node * @param node the node to be evaluated. @@ -142,22 +168,36 @@ export default util.createRule({ } return { - TSArrayType(node) { + TSArrayType(node: TSESTree.TSArrayType) { if ( - option === 'array' || - (option === 'array-simple' && isSimpleType(node.elementType)) + isArrayOption || + (isArraySimpleOption && isSimpleType(node.elementType)) ) { return; } - const messageId = - option === 'generic' - ? 'errorStringGeneric' - : 'errorStringGenericSimple'; const isReadonly = node.parent && node.parent.type === AST_NODE_TYPES.TSTypeOperator && node.parent.operator === 'readonly'; + + const isReadonlyGeneric = + readonlyOption === 'generic' && defaultOption !== 'generic'; + + const isReadonlyArray = + readonlyOption !== 'generic' && defaultOption === 'generic'; + + if ( + (isReadonlyGeneric && !isReadonly) || + (isReadonlyArray && isReadonly) + ) { + return; + } + + const messageId = + defaultOption === 'generic' + ? 'errorStringGeneric' + : 'errorStringGenericSimple'; const typeOpNode = isReadonly ? node.parent! : null; context.report({ @@ -200,23 +240,32 @@ export default util.createRule({ }, }); }, + TSTypeReference(node: TSESTree.TSTypeReference) { if ( - option === 'generic' || + isGenericOption || node.typeName.type !== AST_NODE_TYPES.Identifier ) { return; } - if (!['Array', 'ReadonlyArray'].includes(node.typeName.name)) { + + const isReadonlyArrayType = node.typeName.name === 'ReadonlyArray'; + const isArrayType = node.typeName.name === 'Array'; + + if ( + !(isArrayType || isReadonlyArrayType) || + (readonlyOption === 'generic' && isReadonlyArrayType) || + (defaultOption === 'generic' && !isReadonlyArrayType) + ) { return; } - const messageId = - option === 'array' ? 'errorStringArray' : 'errorStringArraySimple'; - const isReadonly = node.typeName.name === 'ReadonlyArray'; - const readonlyPrefix = isReadonly ? 'readonly ' : ''; - + const readonlyPrefix = isReadonlyArrayType ? 'readonly ' : ''; const typeParams = node.typeParameters && node.typeParameters.params; + const messageId = + defaultOption === 'array' + ? 'errorStringArray' + : 'errorStringArraySimple'; if (!typeParams || typeParams.length === 0) { // Create an 'any' array @@ -230,12 +279,13 @@ export default util.createRule({ return fixer.replaceText(node, `${readonlyPrefix}any[]`); }, }); + return; } if ( typeParams.length !== 1 || - (option === 'array-simple' && !isSimpleType(typeParams[0])) + (defaultOption === 'array-simple' && !isSimpleType(typeParams[0])) ) { return; } diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index 70343f688fc..71482d697e0 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -1,5 +1,5 @@ import { TSESLint } from '@typescript-eslint/experimental-utils'; -import rule from '../../src/rules/array-type'; +import rule, { ArrayOption } from '../../src/rules/array-type'; import { RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ @@ -10,175 +10,201 @@ ruleTester.run('array-type', rule, { valid: [ { code: 'let a: readonly any[] = []', - options: ['array'], + options: [{ default: 'array' }], }, { code: 'let a = new Array()', - options: ['array'], + options: [{ default: 'array' }], }, { code: 'let a: string[] = []', - options: ['array'], + options: [{ default: 'array' }], }, { code: 'let a: (string | number)[] = []', - options: ['array'], + options: [{ default: 'array' }], }, { code: 'let a: ({ foo: Bar[] })[] = []', - options: ['array'], + options: [{ default: 'array' }], }, { code: 'let a: Array = []', - options: ['generic'], + options: [{ default: 'generic' }], }, { - code: 'let a: Array = []', - options: ['generic'], + code: 'let a: Array = []', + options: [{ default: 'generic' }], }, { code: 'let a: Array<{ foo: Array }> = []', - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `let fooVar: Array;`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `function foo (a: Array): Array {}`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `let yy: number[][] = [[4, 5], [6]];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], }, { code: `function fooFunction(foo: Array>) { return foo.map(e => e.foo); }`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], }, { code: `function bazFunction(baz: Arr>) { return baz.map(e => e.baz); }`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], }, { code: `let fooVar: Array<(c: number) => number>;`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], }, { - code: `type fooUnion = Array;`, - options: ['array-simple'], + code: `type fooUnion = Array;`, + options: [{ default: 'array-simple' }], }, { code: `type fooIntersection = Array;`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], }, { code: `namespace fooName { type BarType = { bar: string }; type BazType = Arr; }`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], }, { code: `interface FooInterface { '.bar': {baz: string[];}; }`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], }, { code: `let yy: number[][] = [[4, 5], [6]];`, - options: ['array'], + options: [{ default: 'array' }], }, { code: `let ya = [[1, "2"]] as[number, string][];`, - options: ['array'], + options: [{ default: 'array' }], }, { code: `function barFunction(bar: ArrayClass[]) { return bar.map(e => e.bar); }`, - options: ['array'], + options: [{ default: 'array' }], }, { code: `function bazFunction(baz: Arr>) { return baz.map(e => e.baz); }`, - options: ['array'], + options: [{ default: 'array' }], }, { code: `let barVar: ((c: number) => number)[];`, - options: ['array'], + options: [{ default: 'array' }], }, { code: `type barUnion = (string|number|boolean)[];`, - options: ['array'], + options: [{ default: 'array' }], }, { code: `type barIntersection = (string & number)[];`, - options: ['array'], + options: [{ default: 'array' }], }, { code: `interface FooInterface { '.bar': {baz: string[];}; }`, - options: ['array'], + options: [{ default: 'array' }], }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 code: 'type Unwrap = T extends (infer E)[] ? E : T', - options: ['array'], + options: [{ default: 'array' }], }, { code: `let z: Array = [3, "4"];`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `let xx: Array> = [[1, 2], [3]];`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `type Arr = Array;`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `function fooFunction(foo: Array>) { return foo.map(e => e.foo); }`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `function bazFunction(baz: Arr>) { return baz.map(e => e.baz); }`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `let fooVar: Array<(c: number) => number>;`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `type fooUnion = Array;`, - options: ['generic'], + options: [{ default: 'generic' }], }, { code: `type fooIntersection = Array;`, - options: ['generic'], + options: [{ default: 'generic' }], }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 code: 'type Unwrap = T extends Array ? E : T', - options: ['generic'], + options: [{ default: 'generic' }], + }, + + // readonly + { + code: 'let a: string[] = []', + options: [{ default: 'array', readonly: 'generic' }], + }, + { + code: 'let a: ReadonlyArray = []', + options: [{ default: 'array', readonly: 'generic' }], + }, + { + code: 'let a: ReadonlyArray = [[]]', + options: [{ default: 'array', readonly: 'generic' }], + }, + { + code: `let a: Array = []`, + options: [{ default: 'generic', readonly: 'array' }], + }, + { + code: `let a: readonly number[] = []`, + options: [{ default: 'generic', readonly: 'array' }], + }, + { + code: `let a: readonly Array[] = [[]]`, + options: [{ default: 'generic', readonly: 'array' }], }, ], invalid: [ { code: 'let a: Array = []', output: 'let a: string[] = []', - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -191,7 +217,7 @@ ruleTester.run('array-type', rule, { { code: 'let a: Array = []', output: 'let a: (string | number)[] = []', - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -204,7 +230,7 @@ ruleTester.run('array-type', rule, { { code: 'let a: ({ foo: Array })[] = []', output: 'let a: ({ foo: Bar[] })[] = []', - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -217,7 +243,7 @@ ruleTester.run('array-type', rule, { { code: 'let a: string[] = []', output: 'let a: Array = []', - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -230,7 +256,7 @@ ruleTester.run('array-type', rule, { { code: 'let a: (string | number)[] = []', output: 'let a: Array = []', - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -243,7 +269,7 @@ ruleTester.run('array-type', rule, { { code: 'let a: Array<{ foo: Bar[] }> = []', output: 'let a: Array<{ foo: Array }> = []', - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -256,7 +282,7 @@ ruleTester.run('array-type', rule, { { code: 'let a: Array<{ foo: Foo | Bar[] }> = []', output: 'let a: Array<{ foo: Foo | Array }> = []', - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -269,6 +295,7 @@ ruleTester.run('array-type', rule, { { code: 'function foo (a: Array): Array {}', output: 'function foo (a: Bar[]): Bar[] {}', + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -287,7 +314,7 @@ ruleTester.run('array-type', rule, { { code: `let x: Array = [undefined] as undefined[];`, output: `let x: undefined[] = [undefined] as undefined[];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -300,7 +327,7 @@ ruleTester.run('array-type', rule, { { code: `let xx: Array = [];`, output: `let xx: object[] = [];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -313,7 +340,7 @@ ruleTester.run('array-type', rule, { { code: `let y: string[] = >["2"];`, output: `let y: string[] = ["2"];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -326,7 +353,7 @@ ruleTester.run('array-type', rule, { { code: `let z: Array = [3, "4"];`, output: `let z: any[] = [3, "4"];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -339,7 +366,7 @@ ruleTester.run('array-type', rule, { { code: `let ya = [[1, "2"]] as[number, string][];`, output: `let ya = [[1, "2"]] as Array<[number, string]>;`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', @@ -352,7 +379,7 @@ ruleTester.run('array-type', rule, { { code: `type Arr = Array;`, output: `type Arr = T[];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -367,7 +394,7 @@ ruleTester.run('array-type', rule, { let yyyy: Arr>[]> = [[[["2"]]]];`, output: `// Ignore user defined aliases let yyyy: Arr>>> = [[[["2"]]]];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', @@ -390,7 +417,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, baz: Arr; xyz: this[]; }`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -407,7 +434,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, output: `function barFunction(bar: Array>) { return bar.map(e => e.bar); }`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', @@ -420,7 +447,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `let barVar: ((c: number) => number)[];`, output: `let barVar: Array<(c: number) => number>;`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', @@ -433,7 +460,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `type barUnion = (string|number|boolean)[];`, output: `type barUnion = Array;`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', @@ -446,7 +473,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `type barIntersection = (string & number)[];`, output: `type barIntersection = Array;`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', @@ -459,7 +486,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `let v: Array = [{ bar: "bar" }];`, output: `let v: fooName.BarType[] = [{ bar: "bar" }];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -472,7 +499,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `let w: fooName.BazType[] = [["baz"]];`, output: `let w: Array> = [["baz"]];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringGenericSimple', @@ -485,7 +512,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `let x: Array = [undefined] as undefined[];`, output: `let x: undefined[] = [undefined] as undefined[];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -498,7 +525,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `let y: string[] = >["2"];`, output: `let y: string[] = ["2"];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -511,7 +538,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `let z: Array = [3, "4"];`, output: `let z: any[] = [3, "4"];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -524,7 +551,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `type Arr = Array;`, output: `type Arr = T[];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -539,7 +566,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, let yyyy: Arr>[]> = [[[["2"]]]];`, output: `// Ignore user defined aliases let yyyy: Arr[][]> = [[[["2"]]]];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -560,7 +587,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, bar: T[]; baz: Arr; }`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -577,7 +604,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, output: `function fooFunction(foo: ArrayClass[]) { return foo.map(e => e.foo); }`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -590,7 +617,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, { code: `let fooVar: Array<(c: number) => number>;`, output: `let fooVar: ((c: number) => number)[];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -603,7 +630,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, { code: `type fooUnion = Array;`, output: `type fooUnion = (string|number|boolean)[];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -616,7 +643,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, { code: `type fooIntersection = Array;`, output: `type fooIntersection = (string & number)[];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -628,7 +655,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, }, { code: `let fooVar: Array[];`, - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -640,7 +667,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, }, { code: `let fooVar: Array[];`, - options: ['array-simple'], + options: [{ default: 'array-simple' }], errors: [ { messageId: 'errorStringArraySimple', @@ -653,7 +680,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, { code: `let x: Array = [1] as number[];`, output: `let x: Array = [1] as Array;`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -666,7 +693,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, { code: `let y: string[] = >["2"];`, output: `let y: Array = >["2"];`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -679,7 +706,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, { code: `let ya = [[1, "2"]] as[number, string][];`, output: `let ya = [[1, "2"]] as Array<[number, string]>;`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -694,7 +721,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, let yyyy: Arr>[]> = [[[["2"]]]];`, output: `// Ignore user defined aliases let yyyy: Arr>>> = [[[["2"]]]];`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -715,7 +742,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, bar: Array; baz: Arr; }`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -732,7 +759,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, output: `function barFunction(bar: Array>) { return bar.map(e => e.bar); }`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -745,7 +772,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `let barVar: ((c: number) => number)[];`, output: `let barVar: Array<(c: number) => number>;`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -758,7 +785,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `type barUnion = (string|number|boolean)[];`, output: `type barUnion = Array;`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -771,7 +798,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: `type barIntersection = (string & number)[];`, output: `type barIntersection = Array;`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -788,7 +815,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, output: `interface FooInterface { '.bar': {baz: Array;}; }`, - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -802,7 +829,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, // https://github.com/typescript-eslint/typescript-eslint/issues/172 code: 'type Unwrap = T extends Array ? E : T', output: 'type Unwrap = T extends (infer E)[] ? E : T', - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -816,7 +843,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, // https://github.com/typescript-eslint/typescript-eslint/issues/172 code: 'type Unwrap = T extends (infer E)[] ? E : T', output: 'type Unwrap = T extends Array ? E : T', - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -831,7 +858,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: 'const x: readonly number[] = [];', output: 'const x: ReadonlyArray = [];', - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -844,7 +871,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: 'const x: readonly (number | string | boolean)[] = [];', output: 'const x: ReadonlyArray = [];', - options: ['generic'], + options: [{ default: 'generic' }], errors: [ { messageId: 'errorStringGeneric', @@ -857,7 +884,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: 'const x: ReadonlyArray = [];', output: 'const x: readonly number[] = [];', - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -870,7 +897,7 @@ let yyyy: Arr>>> = [[[["2"]]]];`, { code: 'const x: ReadonlyArray = [];', output: 'const x: readonly (number | string | boolean)[] = [];', - options: ['array'], + options: [{ default: 'array' }], errors: [ { messageId: 'errorStringArray', @@ -880,6 +907,32 @@ let yyyy: Arr>>> = [[[["2"]]]];`, }, ], }, + { + code: 'const x: readonly number[] = []', + output: 'const x: ReadonlyArray = []', + options: [{ default: 'array', readonly: 'generic' }], + errors: [ + { + messageId: 'errorStringGenericSimple', + data: { type: 'number' }, + line: 1, + column: 10, + }, + ], + }, + { + code: 'const x: readonly number[][] = []', + output: 'const x: readonly Array[] = []', + options: [{ default: 'generic', readonly: 'array' }], + errors: [ + { + messageId: 'errorStringGeneric', + data: { type: 'number' }, + line: 1, + column: 19, + }, + ], + }, ], }); @@ -887,7 +940,12 @@ let yyyy: Arr>>> = [[[["2"]]]];`, // https://github.com/eslint/eslint/issues/11187 describe('array-type (nested)', () => { describe('should deeply fix correctly', () => { - function testOutput(option: string, code: string, output: string): void { + function testOutput( + defaultOption: ArrayOption, + code: string, + output: string, + readonlyOption?: ArrayOption, + ): void { it(code, () => { const linter = new TSESLint.Linter(); @@ -896,7 +954,10 @@ describe('array-type (nested)', () => { code, { rules: { - 'array-type': [2, option], + 'array-type': [ + 2, + { default: defaultOption, readonly: readonlyOption }, + ], }, parser: '@typescript-eslint/parser', }, @@ -977,5 +1038,35 @@ class Foo extends Bar implements Baz { `let x: ReadonlyArray`, `let x: readonly (readonly number[])[]`, ); + testOutput( + 'array', + `let x: ReadonlyArray`, + `let x: ReadonlyArray`, + 'generic', + ); + testOutput( + 'array', + `let a: string[] = []`, + `let a: string[] = []`, + 'generic', + ); + testOutput( + 'array', + `let a: readonly number[][] = []`, + `let a: ReadonlyArray = []`, + 'generic', + ); + testOutput( + 'generic', + `let a: string[] = []`, + `let a: Array = []`, + 'array', + ); + testOutput( + 'generic', + `let a: readonly number[][] = []`, + `let a: readonly Array[] = []`, + 'array', + ); }); }); From b78acbb94b59bf6582860256b16955886d092cb0 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 21 Jul 2019 18:36:32 -0700 Subject: [PATCH 2/3] fix tests --- packages/eslint-plugin/tests/rules/array-type.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index ca77f8ba65b..32de7e32c89 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -969,7 +969,6 @@ describe('array-type (nested)', () => { ); expect(result.messages).toHaveLength(0); - expect(result.fixed).toBeTruthy(); expect(result.output).toBe(output); }); } From a357d7b2048119dfb2bc38cabc8b8b144ac51e70 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 21 Jul 2019 19:57:59 -0700 Subject: [PATCH 3/3] docs: update rule readme --- .../eslint-plugin/docs/rules/array-type.md | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/array-type.md b/packages/eslint-plugin/docs/rules/array-type.md index 8906f0c9a4c..275b4fbd617 100644 --- a/packages/eslint-plugin/docs/rules/array-type.md +++ b/packages/eslint-plugin/docs/rules/array-type.md @@ -8,13 +8,26 @@ This rule aims to standardise usage of array types within your codebase. ## Options -This rule accepts one option - a single string +```ts +type ArrayOption = 'array' | 'generic' | 'array-simple'; +type Options = { + default: ArrayOption; + readonly?: ArrayOption; +}; + +const defaultOptions: Options = { + default: 'array', +}; +``` + +The rule accepts an options object with the following properties: + +- `default` - sets the array type expected for mutable cases. +- `readonly` - sets the array type expected for readonly arrays. If this is omitted, then the value for `default` will be used. -- `"array"` enforces use of `T[]` for all types `T`. -- `"generic"` enforces use of `Array` for all types `T`. -- `"array-simple"` enforces use of `T[]` if `T` is a simple type. +Each property can be set to one of three strings: `'array' | 'generic' | 'array-simple'`. -Without providing an option, by default the rule will enforce `"array"`. +The default config will enforce that all mutable and readonly arrays use the `'array'` syntax. ### `"array"`