diff --git a/Jakefile.js b/Jakefile.js
index 5b93462d855..b025bba4087 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -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");
diff --git a/scripts/tslint/typeOperatorSpacingRule.ts b/scripts/tslint/typeOperatorSpacingRule.ts
new file mode 100644
index 00000000000..23925493340
--- /dev/null
+++ b/scripts/tslint/typeOperatorSpacingRule.ts
@@ -0,0 +1,29 @@
+///
+///
+
+
+export class Rule extends Lint.Rules.AbstractRule {
+ public static FAILURE_STRING = "The '|' and '&' operators must be surrounded by single spaces";
+
+ 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 types = (node).types;
+ let expectedStart = types[0].end + 2; // space, | or &
+ for (let i = 1; i < types.length; i++) {
+ let currentType = types[i];
+ if (expectedStart !== currentType.pos || currentType.getLeadingTriviaWidth() !== 1) {
+ const failure = this.createFailure(currentType.pos, currentType.getWidth(), Rule.FAILURE_STRING);
+ this.addFailure(failure);
+ }
+ expectedStart = currentType.end + 2;
+ }
+ }
+ super.visitNode(node);
+ }
+}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index bac0553987d..b3fa607b0ea 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -7031,7 +7031,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 = expr.parent;
@@ -7539,7 +7539,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;
}
@@ -7625,7 +7625,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) {
@@ -7638,7 +7638,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
@@ -7666,7 +7666,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
@@ -7680,7 +7680,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 = node.tagName;
let sym = getResolvedSymbol(tag);
@@ -15547,7 +15547,7 @@ namespace ts {
}
}
- function checkGrammarJsxElement(node: JsxOpeningElement|JsxSelfClosingElement) {
+ function checkGrammarJsxElement(node: JsxOpeningLikeElement) {
const seen: Map = {};
for (let attr of node.attributes) {
if (attr.kind === SyntaxKind.JsxSpreadAttribute) {
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 70ab3606c6c..32f40e7f180 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -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((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) {
+ function emitAttributes(attribs: NodeArray) {
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)) {
@@ -7246,7 +7246,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return emitTemplateSpan(node);
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
- return emitJsxElement(node);
+ return emitJsxElement(node);
case SyntaxKind.JsxText:
return emitJsxText(node);
case SyntaxKind.JsxExpression:
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index d2344722ed5..3e93e31340d 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -3532,7 +3532,7 @@ namespace ts {
return result;
}
- function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement|JsxSelfClosingElement {
+ function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement {
let fullStart = scanner.getStartPos();
parseExpected(SyntaxKind.LessThanToken);
diff --git a/tslint.json b/tslint.json
index 19ccd30ca9f..db10daa3fcc 100644
--- a/tslint.json
+++ b/tslint.json
@@ -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
}
}