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..4143f273dd1
--- /dev/null
+++ b/scripts/tslint/typeOperatorSpacingRule.ts
@@ -0,0 +1,28 @@
+///
+///
+
+
+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 = 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);
+ }
+}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index b31bb6673da..0982d585842 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -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 = 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 = node.tagName;
let sym = getResolvedSymbol(tag);
@@ -15540,7 +15540,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 f2468e7244d..9c9f5f416cb 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)) {
@@ -7241,7 +7241,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 1551706a337..84c5548b283 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -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);
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
}
}