From f85d5f6dbce5a28123e0cc3d01768a47aadcf334 Mon Sep 17 00:00:00 2001 From: Andrii Dieiev Date: Sat, 18 May 2019 02:47:02 +0300 Subject: [PATCH] Improve prefer-array-literal rule (#862) * Use only tslint --test for prefer-array-literal rule * Restrict only global objects with Array (fixes #856) * Add allow-single-argument option and check argument types (fixes #822) * Update docs and metadata for prefer-array-literal rule * Apply suggestions related to wording in README.md Co-Authored-By: Josh Goldberg * Change new option name to allow-size-argument * Use Set for restricted namespaces. Add `self` to restrictions * Fix error message for incorrect size argument * Handle array spread in argument position * Drop node text from all error messages * Handle potential exceptions from TS * Add comment about additional check for SpreadElement --- README.md | 16 ++- src/preferArrayLiteralRule.ts | 108 ++++++++++++++---- src/tests/preferArrayLiteralRuleTests.ts | 93 --------------- src/utils/AstUtils.ts | 2 +- .../allow-size-argument-typed/test.ts.lint | 82 +++++++++++++ .../allow-size-argument-typed/tsconfig.json | 5 + .../allow-size-argument-typed/tslint.json | 5 + .../allow-size-argument/test.ts.lint | 73 ++++++++++++ .../allow-size-argument/tslint.json | 5 + .../allow-type-parameters/test.ts.lint | 74 ++++++++++-- .../allow-type-parameters/tslint.json | 2 +- .../prefer-array-literal/default/test.ts.lint | 80 +++++++++++-- 12 files changed, 407 insertions(+), 138 deletions(-) delete mode 100644 src/tests/preferArrayLiteralRuleTests.ts create mode 100644 tests/prefer-array-literal/allow-size-argument-typed/test.ts.lint create mode 100644 tests/prefer-array-literal/allow-size-argument-typed/tsconfig.json create mode 100644 tests/prefer-array-literal/allow-size-argument-typed/tslint.json create mode 100644 tests/prefer-array-literal/allow-size-argument/test.ts.lint create mode 100644 tests/prefer-array-literal/allow-size-argument/tslint.json diff --git a/README.md b/README.md index 31a2377cb..23c43f5c0 100644 --- a/README.md +++ b/README.md @@ -828,15 +828,19 @@ We recommend you specify exact versions of lint libraries, including `tslint-mic Use array literal syntax when declaring or instantiating array types. For example, prefer the Javascript form of string[] to the TypeScript form Array<string>. - Prefer []' to new Array(). - Prefer [4, 5]' to new Array(4, 5). - Prefer [undefined, undefined]' to new Array(2). - Since 2.0.10, this rule can be configured to allow Array type parameters. - To ignore type parameters, configure the rule with the values: [ true, { 'allow-type-parameters': true } ]. + Prefer [] over new Array(). + Prefer [4, 5] over new Array(4, 5). + Prefer [undefined, undefined] over new Array(2). +
+ Since 2.0.10, this rule can be configured to allow Array type parameters. + To ignore type parameters, configure the rule with the values: [true, {"allow-type-parameters": true}]. +
+ Since @next, you can lift restriction on Array constructor calls with a single argument (to create empty array of a given length). If type information is available - rule will allow only a single argument of number type. + To allow empty array creation, configure the rule with the values: [true, {"allow-size-argument": true}].
This rule has some overlap with the TSLint array-type rule; however, the version here catches more instances. - 1.0, 2.0.10 + 1.0, 2.0.10, @next diff --git a/src/preferArrayLiteralRule.ts b/src/preferArrayLiteralRule.ts index f220186f7..63d3e2caa 100644 --- a/src/preferArrayLiteralRule.ts +++ b/src/preferArrayLiteralRule.ts @@ -6,16 +6,44 @@ import { AstUtils } from './utils/AstUtils'; import { ExtendedMetadata } from './utils/ExtendedMetadata'; import { isObject } from './utils/TypeGuard'; +// undefined for case when function/constructor is called directly without namespace +const RESTRICTED_NAMESPACES: Set = new Set([undefined, 'window', 'self', 'global', 'globalThis']); + +function inRestrictedNamespace(node: ts.NewExpression | ts.CallExpression): boolean { + const functionTarget = AstUtils.getFunctionTarget(node); + return RESTRICTED_NAMESPACES.has(functionTarget); +} + +type InvocationType = 'constructor' | 'function'; + interface Options { + allowSizeArgument: boolean; allowTypeParameters: boolean; } -export class Rule extends Lint.Rules.AbstractRule { + +export class Rule extends Lint.Rules.OptionallyTypedRule { public static metadata: ExtendedMetadata = { ruleName: 'prefer-array-literal', type: 'maintainability', description: 'Use array literal syntax when declaring or instantiating array types.', - options: null, // tslint:disable-line:no-null-keyword - optionsDescription: '', + options: { + type: 'object', + properties: { + 'allow-size-argument': { + type: 'boolean' + }, + 'allow-type-parameters': { + type: 'boolean' + } + }, + additionalProperties: false + }, + optionsDescription: Lint.Utils.dedent` + Rule accepts object with next boolean options: + + - "allow-size-argument" - allows calls to Array constructor with a single element (to create empty array of a given length). + - "allow-type-parameters" - allow Array type parameters. + `, typescriptOnly: true, issueClass: 'Non-SDL', issueType: 'Warning', @@ -25,16 +53,26 @@ export class Rule extends Lint.Rules.AbstractRule { commonWeaknessEnumeration: '398, 710' }; - public static GENERICS_FAILURE_STRING: string = 'Replace generic-typed Array with array literal: '; - public static CONSTRUCTOR_FAILURE_STRING: string = 'Replace Array constructor with an array literal: '; - public static FUNCTION_FAILURE_STRING: string = 'Replace Array function with an array literal: '; + public static GENERICS_FAILURE_STRING: string = 'Replace generic-typed Array with array literal.'; + public static getReplaceFailureString = (type: InvocationType) => `Replace Array ${type} with an array literal.`; + public static getSizeParamFailureString = (type: InvocationType) => + `To create an array of a given length you should use non-negative integer. Otherwise replace Array ${type} with an array literal.`; public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithFunction(sourceFile, walk, this.parseOptions(this.getOptions())); + return this.applyWithProgram(sourceFile, /* program */ undefined); + } + public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program | undefined): Lint.RuleFailure[] { + return this.applyWithFunction( + sourceFile, + walk, + this.parseOptions(this.getOptions()), + program ? program.getTypeChecker() : undefined + ); } private parseOptions(options: Lint.IOptions): Options { - let value: boolean = false; + let allowSizeArgument: boolean = false; + let allowTypeParameters: boolean = false; let ruleOptions: any[] = []; if (options.ruleArguments instanceof Array) { @@ -47,43 +85,67 @@ export class Rule extends Lint.Rules.AbstractRule { ruleOptions.forEach((opt: unknown) => { if (isObject(opt)) { - value = opt['allow-type-parameters'] === true; + allowSizeArgument = opt['allow-size-argument'] === true; + allowTypeParameters = opt['allow-type-parameters'] === true; } }); return { - allowTypeParameters: value + allowSizeArgument, + allowTypeParameters }; } } -function walk(ctx: Lint.WalkContext) { - const { allowTypeParameters } = ctx.options; +function walk(ctx: Lint.WalkContext, checker: ts.TypeChecker | undefined) { + const { allowTypeParameters, allowSizeArgument } = ctx.options; + function checkExpression(type: InvocationType, node: ts.CallExpression | ts.NewExpression): void { + const functionName = AstUtils.getFunctionName(node); + if (functionName === 'Array' && inRestrictedNamespace(node)) { + const callArguments = node.arguments; + if (!allowSizeArgument || !callArguments || callArguments.length !== 1) { + const failureString = Rule.getReplaceFailureString(type); + ctx.addFailureAt(node.getStart(), node.getWidth(), failureString); + } else { + // When typechecker is not available - allow any call with single expression + if (checker) { + try { + // TS might throw exceptions in non-standard conditions (like .vue files) + // Use try...catch blocks to fallback to the same behavior as when checker is not available + // See https://github.com/microsoft/tslint-microsoft-contrib/issues/859 + const argument = callArguments[0]; + const argumentType = checker.getTypeAtLocation(argument); + // Looks like TS returns type or array elements when SpreadElement is passed to getTypeAtLocation. + // Additional check for SpreadElement is required to catch case when array has type number[] and its element will pass assignability check. + // SpreadElement shouldn't be allowed because result of `Array(...arr)` call will dependepend on array lenght and might produce unexpected results. + if (!tsutils.isTypeAssignableToNumber(checker, argumentType) || argument.kind === ts.SyntaxKind.SpreadElement) { + const failureString = Rule.getSizeParamFailureString(type); + ctx.addFailureAt(node.getStart(), node.getWidth(), failureString); + } + } catch { + // No error to use same behavior as when typeChecker is not available + } + } + } + } + } function cb(node: ts.Node): void { if (tsutils.isTypeReferenceNode(node)) { if (!allowTypeParameters) { if ((node.typeName).text === 'Array') { - const failureString = Rule.GENERICS_FAILURE_STRING + node.getText(); + const failureString = Rule.GENERICS_FAILURE_STRING; ctx.addFailureAt(node.getStart(), node.getWidth(), failureString); } } } if (tsutils.isNewExpression(node)) { - const functionName = AstUtils.getFunctionName(node); - if (functionName === 'Array') { - const failureString = Rule.CONSTRUCTOR_FAILURE_STRING + node.getText(); - ctx.addFailureAt(node.getStart(), node.getWidth(), failureString); - } + checkExpression('constructor', node); } if (tsutils.isCallExpression(node)) { - const expr = node.expression; - if (expr.getText() === 'Array') { - const failureString = Rule.FUNCTION_FAILURE_STRING + node.getText(); - ctx.addFailureAt(node.getStart(), node.getWidth(), failureString); - } + checkExpression('function', node); } return ts.forEachChild(node, cb); diff --git a/src/tests/preferArrayLiteralRuleTests.ts b/src/tests/preferArrayLiteralRuleTests.ts deleted file mode 100644 index 9550c599f..000000000 --- a/src/tests/preferArrayLiteralRuleTests.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Utils } from '../utils/Utils'; -import { TestHelper } from './TestHelper'; - -describe('preferArrayLiteralRule', (): void => { - const ruleName: string = 'prefer-array-literal'; - - it('should allow string[] as variable type', (): void => { - const inputScript: string = 'var x : string[];'; - TestHelper.assertViolations(ruleName, inputScript, []); - }); - - it('should allow Array type parameters when options say to ignore type params', (): void => { - const inputScript: string = ` - let myArray: Array = []; - - interface MyInterface { - myArray: Array; - } `; - TestHelper.assertViolationsWithOptions(ruleName, [true, { 'allow-type-parameters': true }], inputScript, []); - }); - - it('should ban Array as variable type', (): void => { - const inputScript: string = 'var x : Array;'; - TestHelper.assertViolations(ruleName, inputScript, [ - { - failure: 'Replace generic-typed Array with array literal: Array', - name: Utils.absolutePath('file.ts'), - ruleName: 'prefer-array-literal', - startPosition: { character: 9, line: 1 } - } - ]); - }); - - it('should ban Array as parameter type', (): void => { - const inputScript: string = 'function (parm: Array) {}'; - TestHelper.assertViolations(ruleName, inputScript, [ - { - failure: 'Replace generic-typed Array with array literal: Array', - name: Utils.absolutePath('file.ts'), - ruleName: 'prefer-array-literal', - startPosition: { character: 17, line: 1 } - } - ]); - }); - - it('should ban new Array() constructor', (): void => { - const inputScript: string = 'new Array()'; - TestHelper.assertViolationsWithOptions(ruleName, [true, { 'allow-type-parameters': true }], inputScript, [ - { - failure: 'Replace Array constructor with an array literal: new Array()', - name: Utils.absolutePath('file.ts'), - ruleName: 'prefer-array-literal', - startPosition: { character: 1, line: 1 } - } - ]); - }); - - it('should ban new Array(4, 5) constructor', (): void => { - const inputScript: string = 'new Array(4, 5)'; - TestHelper.assertViolations(ruleName, inputScript, [ - { - failure: 'Replace Array constructor with an array literal: new Array(4, 5)', - name: Utils.absolutePath('file.ts'), - ruleName: 'prefer-array-literal', - startPosition: { character: 1, line: 1 } - } - ]); - }); - - it('should ban new Array(4) constructor', (): void => { - const inputScript: string = 'new Array(4)'; - TestHelper.assertViolations(ruleName, inputScript, [ - { - failure: 'Replace Array constructor with an array literal: new Array(4)', - name: Utils.absolutePath('file.ts'), - ruleName: 'prefer-array-literal', - startPosition: { character: 1, line: 1 } - } - ]); - }); - - it('should ban Array function', (): void => { - const inputScript: string = 'Array(2)'; - TestHelper.assertViolations(ruleName, inputScript, [ - { - failure: 'Replace Array function with an array literal: Array(2)', - name: Utils.absolutePath('file.ts'), - ruleName: 'prefer-array-literal', - startPosition: { character: 1, line: 1 } - } - ]); - }); -}); diff --git a/src/utils/AstUtils.ts b/src/utils/AstUtils.ts index 09e53040c..f48d38f77 100644 --- a/src/utils/AstUtils.ts +++ b/src/utils/AstUtils.ts @@ -25,7 +25,7 @@ export namespace AstUtils { return ''; } - export function getFunctionTarget(expression: ts.CallExpression): string | undefined { + export function getFunctionTarget(expression: ts.CallExpression | ts.NewExpression): string | undefined { if (expression.expression.kind === ts.SyntaxKind.PropertyAccessExpression) { const propExp: ts.PropertyAccessExpression = expression.expression; return propExp.expression.getText(); diff --git a/tests/prefer-array-literal/allow-size-argument-typed/test.ts.lint b/tests/prefer-array-literal/allow-size-argument-typed/test.ts.lint new file mode 100644 index 000000000..4957f52f0 --- /dev/null +++ b/tests/prefer-array-literal/allow-size-argument-typed/test.ts.lint @@ -0,0 +1,82 @@ +let a: string[]; + +let b: Array = []; + ~~~~~~~~~~~~~ [type] + +interface C { + myArray: Array; + ~~~~~~~~~~~~~ [type] +} + +var d: Array; + ~~~~~~~~~~~~~ [type] + +function e(param: Array) { } + ~~~~~~~~~~~~~ [type] + +var f = new Array(); + ~~~~~~~~~~~ [constructor] + +var g = new Array(4, 5); + ~~~~~~~~~~~~~~~ [constructor] + +var h = new Array(4); + +var i = Array(2); + +var j = new Array; + ~~~~~~~~~ [constructor] + +// calls to Array function/constructor on global objects is forbidden +var nc1 = window.Array(1); +var nc2 = global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~ [function] +var nc3 = globalThis.Array('3'); + ~~~~~~~~~~~~~~~~~~~~~ [size-argument-function] + +var nn1 = new window.Array(1); +var nn2 = new global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~~~~~ [constructor] +var nn3 = new globalThis.Array('3'); + ~~~~~~~~~~~~~~~~~~~~~~~~~ [size-argument-constructor] + +// calls to Array function/constructor from namespaces are valid +import { Types } from 'mongoose'; +export const foo: Types.Array = new Types.Array(); + +declare var num: number; +declare var str: string; +declare var unionA: number | Array; + ~~~~~~~~~~~~~ [type] +declare var unionF: number | (() => number); + +const t1 = Array(num); +const t2 = Array(str); + ~~~~~~~~~~ [size-argument-function] +const t3 = Array(unionA); + ~~~~~~~~~~~~~ [size-argument-function] +const t3 = Array(unionF); + ~~~~~~~~~~~~~ [size-argument-function] +const t4 = Array(num + 1); +const t5 = Array(str + 1); + ~~~~~~~~~~~~~~ [size-argument-function] +const t6 = Array(10 + 1); +const t7 = Array(10 + '1'); + ~~~~~~~~~~~~~~~ [size-argument-function] +const t8 = Array(1.5); // no error - limitation of typed rule +const t9 = Array(-1); // no error - limitation of typed rule +const t10 = Array(-num); // no error - limitation of typed rule + +declare var arrS: number[]; +declare var arrN: number[]; + +const t11 = Array(...arrS); + ~~~~~~~~~~~~~~ [size-argument-function] +const t12 = Array(...arrN); + ~~~~~~~~~~~~~~ [size-argument-function] + +[type]: Replace generic-typed Array with array literal. +[constructor]: Replace Array constructor with an array literal. +[function]: Replace Array function with an array literal. +[size-argument-constructor]: To create an array of a given length you should use non-negative integer. Otherwise replace Array constructor with an array literal. +[size-argument-function]: To create an array of a given length you should use non-negative integer. Otherwise replace Array function with an array literal. diff --git a/tests/prefer-array-literal/allow-size-argument-typed/tsconfig.json b/tests/prefer-array-literal/allow-size-argument-typed/tsconfig.json new file mode 100644 index 000000000..fa3e10a3d --- /dev/null +++ b/tests/prefer-array-literal/allow-size-argument-typed/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "target": "es5" + } +} diff --git a/tests/prefer-array-literal/allow-size-argument-typed/tslint.json b/tests/prefer-array-literal/allow-size-argument-typed/tslint.json new file mode 100644 index 000000000..3cdc603e7 --- /dev/null +++ b/tests/prefer-array-literal/allow-size-argument-typed/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "prefer-array-literal": [true, {"allow-size-argument": true}] + } +} diff --git a/tests/prefer-array-literal/allow-size-argument/test.ts.lint b/tests/prefer-array-literal/allow-size-argument/test.ts.lint new file mode 100644 index 000000000..749caf650 --- /dev/null +++ b/tests/prefer-array-literal/allow-size-argument/test.ts.lint @@ -0,0 +1,73 @@ +let a: string[]; + +let b: Array = []; + ~~~~~~~~~~~~~ [type] + +interface C { + myArray: Array; + ~~~~~~~~~~~~~ [type] +} + +var d: Array; + ~~~~~~~~~~~~~ [type] + +function e(param: Array) { } + ~~~~~~~~~~~~~ [type] + +var f = new Array(); + ~~~~~~~~~~~ [constructor] + +var g = new Array(4, 5); + ~~~~~~~~~~~~~~~ [constructor] + +var h = new Array(4); + +var i = Array(2); + +var j = new Array; + ~~~~~~~~~ [constructor] + +// calls to Array function/constructor on global objects is forbidden +var nc1 = window.Array(1); +var nc2 = global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~ [function] +var nc3 = globalThis.Array('3'); // no error - limitation of untyped rule + +var nn1 = new window.Array(1); +var nn2 = new global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~~~~~ [constructor] +var nn3 = new globalThis.Array('3'); // no error - limitation of untyped rule + +// calls to Array function/constructor from namespaces are valid +import { Types } from 'mongoose'; +export const foo: Types.Array = new Types.Array(); + +declare var num: number; +declare var str: string; +declare var unionA: number | Array; + ~~~~~~~~~~~~~ [type] +declare var unionF: number | (() => number); + +// no errors below - limitation of untyped rule +const t1 = Array(num); +const t2 = Array(str); +const t3 = Array(unionA); +const t3 = Array(unionF); +const t4 = Array(num + 1); +const t5 = Array(str + 1); +const t6 = Array(10 + 1); +const t7 = Array(10 + '1'); +const t8 = Array(1.5); +const t9 = Array(-1); +const t10 = Array(-num); + +declare var arrS: number[]; +declare var arrN: number[]; + +// no errors to match behavior of a regular single argument +const t11 = Array(...arrS); +const t12 = Array(...arrN); + +[type]: Replace generic-typed Array with array literal. +[constructor]: Replace Array constructor with an array literal. +[function]: Replace Array function with an array literal. diff --git a/tests/prefer-array-literal/allow-size-argument/tslint.json b/tests/prefer-array-literal/allow-size-argument/tslint.json new file mode 100644 index 000000000..3cdc603e7 --- /dev/null +++ b/tests/prefer-array-literal/allow-size-argument/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "prefer-array-literal": [true, {"allow-size-argument": true}] + } +} diff --git a/tests/prefer-array-literal/allow-type-parameters/test.ts.lint b/tests/prefer-array-literal/allow-type-parameters/test.ts.lint index 0e086cebf..9205520e8 100644 --- a/tests/prefer-array-literal/allow-type-parameters/test.ts.lint +++ b/tests/prefer-array-literal/allow-type-parameters/test.ts.lint @@ -1,24 +1,84 @@ let a: string[]; let b: Array = []; - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] interface C { myArray: Array; - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] } var d: Array; - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] function e(param: Array) { } - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] var f = new Array(); - ~~~~~~~~~~~ [Replace Array constructor with an array literal: new Array()] + ~~~~~~~~~~~ [constructor] var g = new Array(4, 5); - ~~~~~~~~~~~~~~~ [Replace Array constructor with an array literal: new Array(4, 5)] + ~~~~~~~~~~~~~~~ [constructor] var h = new Array(4); - ~~~~~~~~~~~~ [Replace Array constructor with an array literal: new Array(4)] \ No newline at end of file + ~~~~~~~~~~~~ [constructor] + +var i = Array(2); + ~~~~~~~~ [function] + +var j = new Array; + ~~~~~~~~~ [constructor] + +// calls to Array function/constructor on global objects is forbidden +var nc1 = window.Array(1); + ~~~~~~~~~~~~~~~ [function] +var nc2 = global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~ [function] +var nc3 = globalThis.Array('3'); + ~~~~~~~~~~~~~~~~~~~~~ [function] + +var nn1 = new window.Array(1); + ~~~~~~~~~~~~~~~~~~~ [constructor] +var nn2 = new global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~~~~~ [constructor] +var nn3 = new globalThis.Array('3'); + ~~~~~~~~~~~~~~~~~~~~~~~~~ [constructor] + +// calls to Array function/constructor from namespaces are valid +import { Types } from 'mongoose'; +export const foo: Types.Array = new Types.Array(); + +declare var num: number; +declare var str: string; +declare var unionA: number | Array; +declare var unionF: number | (() => number); + +const t1 = Array(num); + ~~~~~~~~~~ [function] +const t2 = Array(str); + ~~~~~~~~~~ [function] +const t3 = Array(unionA); + ~~~~~~~~~~~~~ [function] +const t3 = Array(unionF); + ~~~~~~~~~~~~~ [function] +const t4 = Array(num + 1); + ~~~~~~~~~~~~~~ [function] +const t5 = Array(str + 1); + ~~~~~~~~~~~~~~ [function] +const t6 = Array(10 + 1); + ~~~~~~~~~~~~~ [function] +const t7 = Array(10 + '1'); + ~~~~~~~~~~~~~~~ [function] +const t8 = Array(1.5); + ~~~~~~~~~~ [function] +const t9 = Array(-1); + ~~~~~~~~~ [function] +const t10 = Array(-num); + ~~~~~~~~~~~ [function] + +declare var arrS: number[]; +declare var arrN: number[]; + +const t11 = Array(...arrS); + ~~~~~~~~~~~~~~ [function] +const t12 = Array(...arrN); + ~~~~~~~~~~~~~~ [function] + +[constructor]: Replace Array constructor with an array literal. +[function]: Replace Array function with an array literal. diff --git a/tests/prefer-array-literal/allow-type-parameters/tslint.json b/tests/prefer-array-literal/allow-type-parameters/tslint.json index 01a80c187..1c60e7d2c 100644 --- a/tests/prefer-array-literal/allow-type-parameters/tslint.json +++ b/tests/prefer-array-literal/allow-type-parameters/tslint.json @@ -1,5 +1,5 @@ { "rules": { - "prefer-array-literal": [true, "allow-type-parameters"] + "prefer-array-literal": [true, {"allow-type-parameters": true}] } } diff --git a/tests/prefer-array-literal/default/test.ts.lint b/tests/prefer-array-literal/default/test.ts.lint index 0e086cebf..fce34787b 100644 --- a/tests/prefer-array-literal/default/test.ts.lint +++ b/tests/prefer-array-literal/default/test.ts.lint @@ -1,24 +1,90 @@ let a: string[]; let b: Array = []; - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] + ~~~~~~~~~~~~~ [type] interface C { myArray: Array; - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] + ~~~~~~~~~~~~~ [type] } var d: Array; - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] + ~~~~~~~~~~~~~ [type] function e(param: Array) { } - ~~~~~~~~~~~~~ [Replace generic-typed Array with array literal: Array] + ~~~~~~~~~~~~~ [type] var f = new Array(); - ~~~~~~~~~~~ [Replace Array constructor with an array literal: new Array()] + ~~~~~~~~~~~ [constructor] var g = new Array(4, 5); - ~~~~~~~~~~~~~~~ [Replace Array constructor with an array literal: new Array(4, 5)] + ~~~~~~~~~~~~~~~ [constructor] var h = new Array(4); - ~~~~~~~~~~~~ [Replace Array constructor with an array literal: new Array(4)] \ No newline at end of file + ~~~~~~~~~~~~ [constructor] + +var i = Array(2); + ~~~~~~~~ [function] + +var j = new Array; + ~~~~~~~~~ [constructor] + +// calls to Array function/constructor on global objects is forbidden +var nc1 = window.Array(1); + ~~~~~~~~~~~~~~~ [function] +var nc2 = global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~ [function] +var nc3 = globalThis.Array('3'); + ~~~~~~~~~~~~~~~~~~~~~ [function] + +var nn1 = new window.Array(1); + ~~~~~~~~~~~~~~~~~~~ [constructor] +var nn2 = new global.Array(1, 2); + ~~~~~~~~~~~~~~~~~~~~~~ [constructor] +var nn3 = new globalThis.Array('3'); + ~~~~~~~~~~~~~~~~~~~~~~~~~ [constructor] + +// calls to Array function/constructor from namespaces are valid +import { Types } from 'mongoose'; +export const foo: Types.Array = new Types.Array(); + +declare var num: number; +declare var str: string; +declare var unionA: number | Array; + ~~~~~~~~~~~~~ [type] +declare var unionF: number | (() => number); + +const t1 = Array(num); + ~~~~~~~~~~ [function] +const t2 = Array(str); + ~~~~~~~~~~ [function] +const t3 = Array(unionA); + ~~~~~~~~~~~~~ [function] +const t3 = Array(unionF); + ~~~~~~~~~~~~~ [function] +const t4 = Array(num + 1); + ~~~~~~~~~~~~~~ [function] +const t5 = Array(str + 1); + ~~~~~~~~~~~~~~ [function] +const t6 = Array(10 + 1); + ~~~~~~~~~~~~~ [function] +const t7 = Array(10 + '1'); + ~~~~~~~~~~~~~~~ [function] +const t8 = Array(1.5); + ~~~~~~~~~~ [function] +const t9 = Array(-1); + ~~~~~~~~~ [function] +const t10 = Array(-num); + ~~~~~~~~~~~ [function] + +declare var arrS: number[]; +declare var arrN: number[]; + +const t11 = Array(...arrS); + ~~~~~~~~~~~~~~ [function] +const t12 = Array(...arrN); + ~~~~~~~~~~~~~~ [function] + +[type]: Replace generic-typed Array with array literal. +[constructor]: Replace Array constructor with an array literal. +[function]: Replace Array function with an array literal.