diff --git a/src/configs/all.ts b/src/configs/all.ts index e719579e734..b2a5b92c039 100644 --- a/src/configs/all.ts +++ b/src/configs/all.ts @@ -214,6 +214,7 @@ export const rules = { "method": "never", "named": "never", }], + "type-literal-delimiter": true, "variable-name": [ true, "ban-keywords", diff --git a/src/rules/adjacentOverloadSignaturesRule.ts b/src/rules/adjacentOverloadSignaturesRule.ts index 83c04d9fc9f..4b573f80212 100644 --- a/src/rules/adjacentOverloadSignaturesRule.ts +++ b/src/rules/adjacentOverloadSignaturesRule.ts @@ -117,7 +117,7 @@ export function getOverloadKey(node: ts.SignatureDeclaration): string | undefine return (computed ? "0" : "1") + (isStatic ? "0" : "1") + name; } -function getOverloadInfo(node: ts.SignatureDeclaration): string | { name: string; computed?: boolean } | undefined { +function getOverloadInfo(node: ts.SignatureDeclaration): string | { name: string, computed?: boolean } | undefined { switch (node.kind) { case ts.SyntaxKind.ConstructSignature: case ts.SyntaxKind.Constructor: diff --git a/src/rules/typeLiteralDelimiterRule.ts b/src/rules/typeLiteralDelimiterRule.ts new file mode 100644 index 00000000000..996e539f0fe --- /dev/null +++ b/src/rules/typeLiteralDelimiterRule.ts @@ -0,0 +1,76 @@ +/** + * @license + * Copyright 2017 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isTypeLiteralNode } from "tsutils"; +import * as ts from "typescript"; + +import * as Lint from "../index"; + +export class Rule extends Lint.Rules.AbstractRule { + /* tslint:disable:object-literal-sort-keys */ + public static metadata: Lint.IRuleMetadata = { + ruleName: "type-literal-delimiter", + description: Lint.Utils.dedent` + Checks that type literal members are separated by commas. + Does not check the last member, as that is done by 'trailing-comma'.`, + optionsDescription: "Not configurable.", + options: null, + optionExamples: [true], + type: "style", + typescriptOnly: true, + }; + /* tslint:enable:object-literal-sort-keys */ + + public static FAILURE_STRING_MISSING = + "Expected type literal to use ',' to separate members."; + public static FAILURE_STRING_SEMICOLON = + "Expected type literal to use ',' instead of ';'."; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, walk); + } +} + +function walk(ctx: Lint.WalkContext): void { + const { sourceFile } = ctx; + ts.forEachChild(sourceFile, function cb(node: ts.Node): void { + if (isTypeLiteralNode(node)) { + check(node); + } + ts.forEachChild(node, cb); + }); + + function check({ members }: ts.TypeLiteralNode): void { + members.forEach((member, idx) => { + const end = member.end - 1; + const delimiter = sourceFile.text[end]; + switch (delimiter) { + case ",": + break; + case ";": + ctx.addFailureAt(end, 1, Rule.FAILURE_STRING_SEMICOLON); + break; + default: + // Allow missing trailing ',' + if (idx !== members.length - 1) { + ctx.addFailureAt(end, 1, Rule.FAILURE_STRING_MISSING); + } + break; + } + }); + } +} diff --git a/test/rules/type-literal-delimiter/test.ts.lint b/test/rules/type-literal-delimiter/test.ts.lint new file mode 100644 index 00000000000..43ae2b8c829 --- /dev/null +++ b/test/rules/type-literal-delimiter/test.ts.lint @@ -0,0 +1,32 @@ +type T = { + x: number + ~ [MISSING] + y: string +} + +type T = { + x: number, + // Doesn't care about missing final comma; trailing-comma will handle. + y: string +} + +// Does care about semicolon. +type T = { + x: number, + y: string; + ~ [SEMI] +} + +type T = { + x: number; + ~ [SEMI] + y: string +} + +// Works even when there's extra whitespace +type T = { x: number , y: number }; +type T = { x: number ; y: number }; + ~ [SEMI] + +[MISSING]: Expected type literal to use ',' to separate members. +[SEMI]: Expected type literal to use ',' instead of ';'. diff --git a/test/rules/type-literal-delimiter/tslint.json b/test/rules/type-literal-delimiter/tslint.json new file mode 100644 index 00000000000..ba33191cc21 --- /dev/null +++ b/test/rules/type-literal-delimiter/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "type-literal-delimiter": true + } +}