Skip to content
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

String literal types #5185

Merged
merged 44 commits into from
Nov 10, 2015
Merged
Changes from 1 commit
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
311a0cf
Added tests.
DanielRosenwasser Sep 11, 2015
911e907
Added test for string literal types in type arguments.
DanielRosenwasser Oct 1, 2015
f04cc39
Accepted baselines.
DanielRosenwasser Oct 1, 2015
191be4f
Make string literals valid constituent types nodes in the parser.
DanielRosenwasser Oct 1, 2015
84786d8
Accepted baselines.
DanielRosenwasser Oct 1, 2015
dc0e368
Make string literals valid types in type lists.
DanielRosenwasser Oct 1, 2015
8891fba
Accepted baselines.
DanielRosenwasser Oct 1, 2015
82545ce
Added test for string types in tuples.
DanielRosenwasser Oct 1, 2015
ed927d8
Accepted baselines.
DanielRosenwasser Oct 1, 2015
a3e7ccb
Use normalized text for text on string literal types.
DanielRosenwasser Oct 2, 2015
20c2c4e
Amended fourslash tests to expect double quotes.
DanielRosenwasser Oct 2, 2015
87f2957
Accepted baselines.
DanielRosenwasser Oct 2, 2015
f721971
Capture compatible contextual types for unions containing string lite…
DanielRosenwasser Oct 2, 2015
7b4e94d
Accepted baselines.
DanielRosenwasser Oct 2, 2015
d8d72aa
Separated the concept of apparent types from contextual types for str…
DanielRosenwasser Oct 2, 2015
315b06d
Accepted baselines.
DanielRosenwasser Oct 2, 2015
4b736da
Fixed issue in test.
DanielRosenwasser Oct 2, 2015
fd5dec4
Accepted baselines.
DanielRosenwasser Oct 2, 2015
f7a6ac7
Added more tests.
DanielRosenwasser Oct 7, 2015
a440f06
Accepted baselines.
DanielRosenwasser Oct 8, 2015
d2e2a55
Added fourslash test.
DanielRosenwasser Oct 8, 2015
6e3343c
Merge branch 'master' into stringLiteralTypes
DanielRosenwasser Oct 8, 2015
74ac57d
Accepted post-merge baselines.
DanielRosenwasser Oct 8, 2015
61ece76
Return the string literal type itself instead of the union type.
DanielRosenwasser Oct 8, 2015
84b64c4
Accepted baselines.
DanielRosenwasser Oct 8, 2015
3788254
Semicolon.
DanielRosenwasser Oct 8, 2015
ebc47d5
Linting.
DanielRosenwasser Oct 8, 2015
725bda8
Merge branch 'master' into stringLiteralTypes
DanielRosenwasser Oct 15, 2015
ec0d49a
Always use a string literal type if contextually typed by any string …
DanielRosenwasser Oct 15, 2015
1dbd8d1
Accepted baselines.
DanielRosenwasser Oct 15, 2015
307d73e
Merge branch 'master' into stringLiteralTypes
DanielRosenwasser Oct 22, 2015
049d02f
Merge branch 'master' into stringLiteralTypes
DanielRosenwasser Oct 23, 2015
6618fd2
Added tests for operations that use assignable to/from.
DanielRosenwasser Oct 26, 2015
bf4880a
Merge branch 'master' into stringLiteralTypes
DanielRosenwasser Oct 26, 2015
5e23143
Accepted baselines.
DanielRosenwasser Oct 27, 2015
8dbfe1c
Added specific checks for comparing stringlike types.
DanielRosenwasser Nov 6, 2015
a1fcfaf
Accepted baselines.
DanielRosenwasser Nov 6, 2015
bb232f7
Merge remote-tracking branch 'origin/master' into stringLiteralTypes
DanielRosenwasser Nov 6, 2015
f939ff2
Fixed unreachable code in tests.
DanielRosenwasser Nov 6, 2015
d234b8d
Accepted baselines.
DanielRosenwasser Nov 6, 2015
c011ed4
Const.
DanielRosenwasser Nov 6, 2015
38090c6
Added tests for template strings with string literal types.
DanielRosenwasser Nov 9, 2015
d294524
Accepted baselines.
DanielRosenwasser Nov 9, 2015
ea4e21d
Fixed comments.
DanielRosenwasser Nov 9, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Separated the concept of apparent types from contextual types for str…
…ing literal types.

In most cases, expressions are interested in the apparent type of the
contextual type. For instance:

    var x = { hasOwnProperty(prop) { /* ... */ };

In the above, 'prop' should be contextually typed as 'string' from the
signature of 'hasOwnProperty' in the global 'Object' type.

However, in the case of string literal types, we don't want to get the
apparent type after fetching the contextual type. This is because the
apparent type of the '"onload"' string literal type is the global 'String'
type. This has adverse effects in simple assignments like the following:

    let x: "onload" = "onload";

In this example, the right-hand side of the assignment will grab the type
of 'x'. After figuring out the type is "onload", we then get the apparent
type which is 'String'. This is problematic because when we then check the
assignment itself, 'String's are not assignable to '"onload"'s.

So in this case, we grab the contextual type *without* getting its
apparent type.
  • Loading branch information
DanielRosenwasser committed Oct 2, 2015
commit d8d72aabae439e17d18e35887981165413b71556
39 changes: 23 additions & 16 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace ts {
symbolToString,
getAugmentedPropertiesOfType,
getRootSymbols,
getContextualType,
getContextualType: getApparentTypeOfContextualType,
getFullyQualifiedName,
getResolvedSignature,
getConstantValue,
Expand Down Expand Up @@ -6716,7 +6716,7 @@ namespace ts {
else if (operator === SyntaxKind.BarBarToken) {
// When an || expression has a contextual type, the operands are contextually typed by that type. When an ||
// expression has no contextual type, the right operand is contextually typed by the type of the left operand.
let type = getContextualType(binaryExpression);
let type = getApparentTypeOfContextualType(binaryExpression);
if (!type && node === binaryExpression.right) {
type = checkExpression(binaryExpression.left);
}
Expand Down Expand Up @@ -6788,7 +6788,7 @@ namespace ts {

function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElement) {
let objectLiteral = <ObjectLiteralExpression>element.parent;
let type = getContextualType(objectLiteral);
let type = getApparentTypeOfContextualType(objectLiteral);
if (type) {
if (!hasDynamicName(element)) {
// For a (non-symbol) computed property, there is no reason to look up the name
Expand All @@ -6814,7 +6814,7 @@ namespace ts {
// type of T.
function getContextualTypeForElementExpression(node: Expression): Type {
let arrayLiteral = <ArrayLiteralExpression>node.parent;
let type = getContextualType(arrayLiteral);
let type = getApparentTypeOfContextualType(arrayLiteral);
if (type) {
let index = indexOf(arrayLiteral.elements, node);
return getTypeOfPropertyOfContextualType(type, "" + index)
Expand All @@ -6827,7 +6827,7 @@ namespace ts {
// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
function getContextualTypeForConditionalOperand(node: Expression): Type {
let conditional = <ConditionalExpression>node.parent;
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
return node === conditional.whenTrue || node === conditional.whenFalse ? getApparentTypeOfContextualType(conditional) : undefined;
}

function getContextualTypeForJsxExpression(expr: JsxExpression|JsxSpreadAttribute): Type {
Expand All @@ -6852,12 +6852,22 @@ namespace ts {

// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getContextualType(node: Expression): Type {
let type = getContextualTypeWorker(node);
function getApparentTypeOfContextualType(node: Expression): Type {
let type = getContextualType(node);
return type && getApparentType(type);
}

function getContextualTypeWorker(node: Expression): Type {
/**
* Woah! Do you really want to use this function?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... that was why I named it Worker...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense, though it feels strange to get the actual contextual type when its name is worker. If you think it'd be better to rename it back to worker, I can do that.

I also suspect that we could shave off some time by using this function (instead of getApparentTypeOfContextualType) for array literals as well. I'll probably do it as part of another change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea (however it is achieved) is to make the apparent type the default behavior when you don't specify anything. Getting the contextual type without its apparent type is quite an odd thing to do, so I prefer that to be the marked form. You can also call them getContextualType and getContextualTypeWithoutApparentType.

*
* Unless you're trying to get the *non-apparent* type for a value-literal type,
* you probably meant to use 'getApparentTypeOfContextualType'.
* Otherwise this is slightly less useful.
*
* @param node the expression whose contextual type will be returned.
* @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;
Expand Down Expand Up @@ -6896,7 +6906,7 @@ namespace ts {
Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression);
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
case SyntaxKind.ParenthesizedExpression:
return getContextualType(<ParenthesizedExpression>parent);
return getApparentTypeOfContextualType(<ParenthesizedExpression>parent);
case SyntaxKind.JsxExpression:
case SyntaxKind.JsxSpreadAttribute:
return getContextualTypeForJsxExpression(<JsxExpression>parent);
Expand Down Expand Up @@ -6936,7 +6946,7 @@ namespace ts {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
let type = isObjectLiteralMethod(node)
? getContextualTypeForObjectLiteralMethod(node)
: getContextualType(node);
: getApparentTypeOfContextualType(node);
if (!type) {
return undefined;
}
Expand Down Expand Up @@ -7066,7 +7076,7 @@ namespace ts {
type.pattern = node;
return type;
}
let contextualType = getContextualType(node);
let contextualType = getApparentTypeOfContextualType(node);
if (contextualType && contextualTypeIsTupleLikeType(contextualType)) {
let pattern = contextualType.pattern;
// If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting
Expand Down Expand Up @@ -7157,7 +7167,7 @@ namespace ts {

let propertiesTable: SymbolTable = {};
let propertiesArray: Symbol[] = [];
let contextualType = getContextualType(node);
let contextualType = getApparentTypeOfContextualType(node);
let contextualTypeHasPattern = contextualType && contextualType.pattern &&
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
let inDestructuringPattern = isAssignmentTarget(node);
Expand Down Expand Up @@ -10116,9 +10126,6 @@ namespace ts {
}
}
else if (contextualType.flags & TypeFlags.StringLiteral && (<StringLiteralType>contextualType).text === node.text) {
// NOTE: This doesn't work because the contextual type of a string literal
// always gets its apparent type.
// Thus you'll always end up with 'String' instead of the literal.
return contextualType;
}
}
Expand Down Expand Up @@ -10184,7 +10191,7 @@ namespace ts {
if (isInferentialContext(contextualMapper)) {
let signature = getSingleCallSignature(type);
if (signature && signature.typeParameters) {
let contextualType = getContextualType(<Expression>node);
let contextualType = getApparentTypeOfContextualType(<Expression>node);
if (contextualType) {
let contextualSignature = getSingleCallSignature(contextualType);
if (contextualSignature && !contextualSignature.typeParameters) {
Expand Down