Add rule to check spaces around intersection/union type operators

This commit is contained in:
Ryan Cavanaugh 2015-10-22 11:35:48 -07:00
parent 28c2887d50
commit 69236c9be8
6 changed files with 46 additions and 16 deletions

View file

@ -820,7 +820,8 @@ var tslintRuleDir = "scripts/tslint";
var tslintRules = ([
"nextLineRule",
"noNullRule",
"booleanTriviaRule"
"booleanTriviaRule",
"typeOperatorSpacingRule"
]);
var tslintRulesFiles = tslintRules.map(function(p) {
return path.join(tslintRuleDir, p + ".ts");

View file

@ -0,0 +1,28 @@
/// <reference path="../../node_modules/tslint/typings/typescriptServices.d.ts" />
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" />
export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = "Place spaces around the '|' and '&' type operators";;
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new TypeOperatorSpacingWalker(sourceFile, this.getOptions()));
}
}
class TypeOperatorSpacingWalker extends Lint.RuleWalker {
public visitNode(node: ts.Node) {
if(node.kind === ts.SyntaxKind.UnionType || node.kind === ts.SyntaxKind.IntersectionType) {
let typeNode = <ts.UnionOrIntersectionTypeNode>node;
let expectedStart = typeNode.types[0].end + 2; // space, | or &
for (let i = 1; i < typeNode.types.length; i++) {
if(expectedStart !== typeNode.types[i].pos || typeNode.types[i].getLeadingTriviaWidth() !== 1) {
const failure = this.createFailure(typeNode.types[i].pos, typeNode.types[i].getWidth(), Rule.FAILURE_STRING);
this.addFailure(failure);
}
expectedStart = typeNode.types[i].end + 2;
}
}
super.visitNode(node);
}
}

View file

@ -7021,7 +7021,7 @@ namespace ts {
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
}
function getContextualTypeForJsxExpression(expr: JsxExpression|JsxSpreadAttribute): Type {
function getContextualTypeForJsxExpression(expr: JsxExpression | JsxSpreadAttribute): Type {
// Contextual type only applies to JSX expressions that are in attribute assignments (not in 'Children' positions)
if (expr.parent.kind === SyntaxKind.JsxAttribute) {
let attrib = <JsxAttribute>expr.parent;
@ -7532,7 +7532,7 @@ namespace ts {
/**
* Returns true iff React would emit this tag name as a string rather than an identifier or qualified name
*/
function isJsxIntrinsicIdentifier(tagName: Identifier|QualifiedName) {
function isJsxIntrinsicIdentifier(tagName: Identifier | QualifiedName) {
if (tagName.kind === SyntaxKind.QualifiedName) {
return false;
}
@ -7618,7 +7618,7 @@ namespace ts {
/// If this is a class-based tag (otherwise returns undefined), returns the symbol of the class
/// type or factory function.
/// Otherwise, returns unknownSymbol.
function getJsxElementTagSymbol(node: JsxOpeningLikeElement|JsxClosingElement): Symbol {
function getJsxElementTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
let flags: JsxFlags = JsxFlags.UnknownElement;
let links = getNodeLinks(node);
if (!links.resolvedSymbol) {
@ -7631,7 +7631,7 @@ namespace ts {
}
return links.resolvedSymbol;
function lookupIntrinsicTag(node: JsxOpeningLikeElement|JsxClosingElement): Symbol {
function lookupIntrinsicTag(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
let intrinsicElementsType = getJsxIntrinsicElementsType();
if (intrinsicElementsType !== unknownType) {
// Property case
@ -7659,7 +7659,7 @@ namespace ts {
}
}
function lookupClassTag(node: JsxOpeningLikeElement|JsxClosingElement): Symbol {
function lookupClassTag(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
let valueSymbol: Symbol = resolveJsxTagName(node);
// Look up the value in the current scope
@ -7673,7 +7673,7 @@ namespace ts {
return valueSymbol || unknownSymbol;
}
function resolveJsxTagName(node: JsxOpeningLikeElement|JsxClosingElement): Symbol {
function resolveJsxTagName(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
if (node.tagName.kind === SyntaxKind.Identifier) {
let tag = <Identifier>node.tagName;
let sym = getResolvedSymbol(tag);
@ -15540,7 +15540,7 @@ namespace ts {
}
}
function checkGrammarJsxElement(node: JsxOpeningElement|JsxSelfClosingElement) {
function checkGrammarJsxElement(node: JsxOpeningLikeElement) {
const seen: Map<boolean> = {};
for (let attr of node.attributes) {
if (attr.kind === SyntaxKind.JsxSpreadAttribute) {

View file

@ -1404,10 +1404,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emit(span.literal);
}
function jsxEmitReact(node: JsxElement|JsxSelfClosingElement) {
function jsxEmitReact(node: JsxElement | JsxSelfClosingElement) {
/// Emit a tag name, which is either '"div"' for lower-cased names, or
/// 'Div' for upper-cased or dotted names
function emitTagName(name: Identifier|QualifiedName) {
function emitTagName(name: Identifier | QualifiedName) {
if (name.kind === SyntaxKind.Identifier && isIntrinsicJsxName((<Identifier>name).text)) {
write("\"");
emit(name);
@ -1557,7 +1557,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
}
function jsxEmitPreserve(node: JsxElement|JsxSelfClosingElement) {
function jsxEmitPreserve(node: JsxElement | JsxSelfClosingElement) {
function emitJsxAttribute(node: JsxAttribute) {
emit(node.name);
if (node.initializer) {
@ -1572,7 +1572,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write("}");
}
function emitAttributes(attribs: NodeArray<JsxAttribute|JsxSpreadAttribute>) {
function emitAttributes(attribs: NodeArray<JsxAttribute | JsxSpreadAttribute>) {
for (let i = 0, n = attribs.length; i < n; i++) {
if (i > 0) {
write(" ");
@ -1588,7 +1588,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
}
function emitJsxOpeningOrSelfClosingElement(node: JsxOpeningElement|JsxSelfClosingElement) {
function emitJsxOpeningOrSelfClosingElement(node: JsxOpeningElement | JsxSelfClosingElement) {
write("<");
emit(node.tagName);
if (node.attributes.length > 0 || (node.kind === SyntaxKind.JsxSelfClosingElement)) {
@ -7241,7 +7241,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return emitTemplateSpan(<TemplateSpan>node);
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
return emitJsxElement(<JsxElement|JsxSelfClosingElement>node);
return emitJsxElement(<JsxElement | JsxSelfClosingElement>node);
case SyntaxKind.JsxText:
return emitJsxText(<JsxText>node);
case SyntaxKind.JsxExpression:

View file

@ -3512,7 +3512,7 @@ namespace ts {
return result;
}
function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement|JsxSelfClosingElement {
function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement {
let fullStart = scanner.getStartPos();
parseExpected(SyntaxKind.LessThanToken);

View file

@ -38,6 +38,7 @@
"no-trailing-whitespace": true,
"no-inferrable-types": true,
"no-null": true,
"boolean-trivia": true
"boolean-trivia": true,
"type-operator-spacing": true
}
}