Skip to content

Commit f86a730

Browse files
author
Kanchalai Tanglertsampan
committed
Consider whitespace that won't be emitted to be different kind so that we won't include in typ-checking
1 parent b3846bf commit f86a730

File tree

4 files changed

+33
-12
lines changed

4 files changed

+33
-12
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13275,7 +13275,7 @@ namespace ts {
1327513275
const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ?
1327613276
openingLikeElement.parent as JsxElement : undefined;
1327713277
let containsSynthesizedJsxChildren = false;
13278-
// Comment
13278+
// We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement
1327913279
if (parent && parent.openingElement === openingLikeElement && parent.children.length > 0) {
1328013280
// Error if there is a attribute named "children" and children element.
1328113281
// This is because children element will overwrite the value from attributes
@@ -13287,7 +13287,11 @@ namespace ts {
1328713287
const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName);
1328813288
const childrenTypes: Type[] = [];
1328913289
for (const child of (parent as JsxElement).children) {
13290-
childrenTypes.push(child.kind === SyntaxKind.JsxText ? stringType : checkExpression(child));
13290+
// In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that
13291+
// because then type of children property will have constituent of string type.
13292+
if (child.kind !== SyntaxKind.JsxTextAllWhiteSpaces) {
13293+
childrenTypes.push(child.kind === SyntaxKind.JsxText ? stringType : checkExpression(child as Expression, checkMode));
13294+
}
1329113295
}
1329213296
childrenPropSymbol.type = getUnionType(childrenTypes, /*subtypeReduction*/ false);
1329313297
attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol);
@@ -17099,13 +17103,6 @@ namespace ts {
1709917103
return cache ? checkExpressionCached(node) : checkExpression(node);
1710017104
}
1710117105

17102-
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
17103-
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
17104-
// expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in
17105-
// conjunction with the generic contextual type. When contextualMapper is equal to the identityMapper function
17106-
// object, it serves as an indicator that all contained function and arrow expressions should be considered to
17107-
// have the wildcard function type; this form of type check is used during overload resolution to exclude
17108-
// contextually typed function and arrow expressions in the initial phase.
1710917106
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
1711017107
let type: Type;
1711117108
if (node.kind === SyntaxKind.QualifiedName) {

src/compiler/parser.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3825,14 +3825,15 @@ namespace ts {
38253825
}
38263826

38273827
function parseJsxText(): JsxText {
3828-
const node = <JsxText>createNode(SyntaxKind.JsxText, scanner.getStartPos());
3828+
const node = <JsxText>createNode(currentToken, scanner.getStartPos());
38293829
currentToken = scanner.scanJsxToken();
38303830
return finishNode(node);
38313831
}
38323832

38333833
function parseJsxChild(): JsxChild {
38343834
switch (token()) {
38353835
case SyntaxKind.JsxText:
3836+
case SyntaxKind.JsxTextAllWhiteSpaces:
38363837
return parseJsxText();
38373838
case SyntaxKind.OpenBraceToken:
38383839
return parseJsxExpression(/*inExpressionContext*/ false);
@@ -3862,7 +3863,10 @@ namespace ts {
38623863
else if (token() === SyntaxKind.ConflictMarkerTrivia) {
38633864
break;
38643865
}
3865-
result.push(parseJsxChild());
3866+
const child = parseJsxChild();
3867+
if (child) {
3868+
result.push(child);
3869+
}
38663870
}
38673871

38683872
result.end = scanner.getTokenPos();

src/compiler/scanner.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1723,6 +1723,11 @@ namespace ts {
17231723
return token = SyntaxKind.OpenBraceToken;
17241724
}
17251725

1726+
// First non-whitespace character on this line.
1727+
let firstNonWhitespace = 0;
1728+
// These initial values are special because the first line is:
1729+
// firstNonWhitespace = 0 to indicate that we want leading whitspace,
1730+
17261731
while (pos < end) {
17271732
pos++;
17281733
char = text.charCodeAt(pos);
@@ -1736,8 +1741,22 @@ namespace ts {
17361741
}
17371742
break;
17381743
}
1744+
1745+
// FirstNonWhitespace is 0, then we only see whitespaces so far. If we see a linebreak, we want to ignore that whitespaces.
1746+
// i.e (- : whitespace)
1747+
// <div>----
1748+
// </div> becomes <div></div>
1749+
//
1750+
// <div>----</div> becomes <div>----</div>
1751+
if (isLineBreak(char) && firstNonWhitespace === 0) {
1752+
firstNonWhitespace = -1;
1753+
}
1754+
else if (!isWhiteSpaceSingleLine(char)) {
1755+
firstNonWhitespace = pos;
1756+
}
17391757
}
1740-
return token = SyntaxKind.JsxText;
1758+
1759+
return firstNonWhitespace === -1 ? SyntaxKind.JsxTextAllWhiteSpaces : SyntaxKind.JsxText;
17411760
}
17421761

17431762
// Scans a JSX identifier; these differ from normal identifiers in that

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
NumericLiteral,
6666
StringLiteral,
6767
JsxText,
68+
JsxTextAllWhiteSpaces,
6869
RegularExpressionLiteral,
6970
NoSubstitutionTemplateLiteral,
7071
// Pseudo-literals

0 commit comments

Comments
 (0)