Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 13.7k
Infer string literals at comparison locations#6196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
0d2fb26639d9bf13ec5d7e109b17de9789ad0a8573e452955ced65ac881b52d836541058580ed069ff332874156e2ddb29f7e9135e6bd7ad16fe01bbc34ebb4eced9001cc2f1259a3cfcc2ab555b9e5d1fdd7fde09d1762dec70f11dd43faab2558496731526b8bac3740792cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -8820,11 +8820,13 @@ namespace ts { | ||
| function getContextualTypeForBinaryOperand(node: Expression): Type { | ||
| const binaryExpression = <BinaryExpression>node.parent; | ||
| const operator = binaryExpression.operatorToken.kind; | ||
| if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { | ||
| if (SyntaxKind.FirstAssignment <= operator && operator <= SyntaxKind.LastAssignment) { | ||
| // In an assignment expression, the right operand is contextually typed by the type of the left operand. | ||
| if (node === binaryExpression.right) { | ||
| return checkExpression(binaryExpression.left); | ||
| } | ||
| return undefined; | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably don't need this | ||
| } | ||
| else if (operator === SyntaxKind.BarBarToken) { | ||
| // When an || expression has a contextual type, the operands are contextually typed by that type. When an || | ||
| @@ -8991,6 +8993,7 @@ namespace ts { | ||
| * @returns the contextual type of an expression. | ||
| */ | ||
| function getContextualType(node: Expression): Type { | ||
| if (isInsideWithStatementBody(node)) { | ||
| // We cannot answer semantic questions within a with block, do not proceed any further | ||
| return undefined; | ||
| @@ -9039,6 +9042,75 @@ namespace ts { | ||
| return undefined; | ||
| } | ||
| function shouldAcquireLiteralType(literalNode: LiteralExpression) { | ||
| if (isEqualityComparisonOperand(literalNode)) { | ||
| return true; | ||
| } | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove whitespace Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And elsewhere in this file. MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whitespace here and below is intentional and it's for readability so that everything doesn't look like one big clump of code. | ||
| const contextualType = getContextualType(literalNode); | ||
| return !!contextualType && contextualTypeIsStringLiteralType(contextualType); | ||
| } | ||
| /** | ||
| * Returns true if an expression might be evaluated as part of an equality comparison. | ||
| * This includes inequality (e.g. '!==') and 'switch'/'case' equality. | ||
| */ | ||
| function isEqualityComparisonOperand(expression: Expression): boolean { | ||
| const parent = expression.parent; | ||
| switch (parent.kind) { | ||
| // The operand of a 'switch' should get a literal type. | ||
| case SyntaxKind.SwitchStatement: | ||
| return expression === (parent as SwitchStatement).expression; | ||
| // The tested expression of a 'case' clause should get a literal type. | ||
| case SyntaxKind.CaseClause: | ||
| return expression === (parent as CaseClause).expression; | ||
| case SyntaxKind.BinaryExpression: | ||
| const binaryExpr = parent as BinaryExpression; | ||
| switch (binaryExpr.operatorToken.kind) { | ||
| // Either operand of an equality/inequality comparison | ||
| // should get a literal type. | ||
| case SyntaxKind.EqualsEqualsEqualsToken: | ||
| case SyntaxKind.ExclamationEqualsEqualsToken: | ||
| case SyntaxKind.EqualsEqualsToken: | ||
| case SyntaxKind.ExclamationEqualsToken: | ||
| return true; | ||
| case SyntaxKind.AmpersandAmpersandToken: | ||
| case SyntaxKind.CommaToken: | ||
| if (expression === binaryExpr.right) { | ||
| return isEqualityComparisonOperand(binaryExpr); | ||
| } | ||
| return false; | ||
| case SyntaxKind.BarBarToken: | ||
| return isEqualityComparisonOperand(binaryExpr); | ||
| } | ||
| // No binary operators apply. | ||
| return false; | ||
| case SyntaxKind.ConditionalExpression: | ||
| const conditional = parent as ConditionalExpression; | ||
| if (expression === conditional.whenTrue || expression === conditional.whenFalse) { | ||
| return isEqualityComparisonOperand(conditional); | ||
| } | ||
| return false; | ||
| case SyntaxKind.ParenthesizedExpression: | ||
| return isEqualityComparisonOperand(parent as ParenthesizedExpression); | ||
| case SyntaxKind.TypeAssertionExpression: | ||
| case SyntaxKind.AsExpression: | ||
| return isEqualityComparisonOperand(parent as TypeAssertion); | ||
| } | ||
| return false; | ||
| } | ||
| // If the given type is an object or union type, if that type has a single signature, and if | ||
| // that signature is non-generic, return the signature. Otherwise return undefined. | ||
| function getNonGenericSignature(type: Type): Signature { | ||
| @@ -11472,6 +11544,7 @@ namespace ts { | ||
| const targetType = getTypeFromTypeNode(node.type); | ||
| if (produceDiagnostics && targetType !== unknownType) { | ||
| const widenedType = getWidenedType(exprType); | ||
| if (!isTypeComparableTo(targetType, widenedType)) { | ||
| checkTypeComparableTo(exprType, targetType, node, Diagnostics.Type_0_cannot_be_converted_to_type_1); | ||
| } | ||
| @@ -12546,8 +12619,7 @@ namespace ts { | ||
| } | ||
| function checkStringLiteralExpression(node: StringLiteral): Type { | ||
| const contextualType = getContextualType(node); | ||
| if (contextualType && contextualTypeIsStringLiteralType(contextualType)) { | ||
| if (shouldAcquireLiteralType(node)) { | ||
| return getStringLiteralTypeForText(node.text); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Purely stylistic, but everywhere else in the compiler we always put the constant on the right hand side (as the code was originally written). I'd prefer to keep things consistent.