Optional chaining prototype

This commit is contained in:
Ron Buckton
2017-09-26 15:13:46 -04:00
parent 4221fb6a39
commit 650fd3870c
10 changed files with 318 additions and 75 deletions
+14 -1
View File
@@ -2688,6 +2688,10 @@ namespace ts {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (node.flags & NodeFlags.Optional) {
transformFlags |= TransformFlags.AssertESNext;
}
if (subtreeFlags & TransformFlags.ContainsSpread
|| isSuperOrSuperProperty(expression, expressionKind)) {
// If the this node contains a SpreadExpression, or is a super call, then it is an ES6
@@ -3132,6 +3136,10 @@ namespace ts {
const expression = node.expression;
const expressionKind = expression.kind;
if (node.flags & NodeFlags.Optional) {
transformFlags |= TransformFlags.AssertESNext;
}
// If a PropertyAccessExpression starts with a super keyword, then it is
// ES6 syntax, and requires a lexical `this` binding.
if (expressionKind === SyntaxKind.SuperKeyword) {
@@ -3449,7 +3457,6 @@ namespace ts {
break;
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.NewExpression:
excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes;
if (subtreeFlags & TransformFlags.ContainsSpread) {
// If the this node contains a SpreadExpression, then it is an ES6
@@ -3459,6 +3466,12 @@ namespace ts {
break;
case SyntaxKind.ElementAccessExpression:
if (node.flags & NodeFlags.Optional) {
transformFlags |= TransformFlags.AssertESNext;
}
break;
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForStatement:
+82 -26
View File
@@ -251,6 +251,7 @@ namespace ts {
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
const optionalType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.Optional | TypeFlags.ContainsWideningType, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null, "null");
const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
const stringType = createIntrinsicType(TypeFlags.String, "string");
@@ -379,19 +380,21 @@ namespace ts {
TypeofNEFunction = 1 << 12, // typeof x !== "function"
TypeofNEHostObject = 1 << 13, // typeof x !== "xxx"
EQUndefined = 1 << 14, // x === undefined
EQNull = 1 << 15, // x === null
EQUndefinedOrNull = 1 << 16, // x === undefined / x === null
NEUndefined = 1 << 17, // x !== undefined
NENull = 1 << 18, // x !== null
NEUndefinedOrNull = 1 << 19, // x != undefined / x != null
Truthy = 1 << 20, // x
Falsy = 1 << 21, // !x
Discriminatable = 1 << 22, // May have discriminant property
All = (1 << 23) - 1,
EQOptional = 1 << 15,
EQNull = 1 << 16, // x === null
EQUndefinedOrNull = 1 << 17, // x === undefined / x === null
NEUndefined = 1 << 18, // x !== undefined
NEOptional = 1 << 19,
NENull = 1 << 20, // x !== null
NEUndefinedOrNull = 1 << 21, // x != undefined / x != null
Truthy = 1 << 22, // x
Falsy = 1 << 23, // !x
Discriminatable = 1 << 24, // May have discriminant property
All = (1 << 25) - 1,
// The following members encode facts about particular kinds of types for use in the getTypeFacts function.
// The presence of a particular fact means that the given test is true for some (and possibly all) values
// of that kind of type.
BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull,
BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy,
StringFacts = BaseStringFacts | Truthy,
@@ -399,7 +402,7 @@ namespace ts {
EmptyStringFacts = BaseStringFacts,
NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy,
NonEmptyStringFacts = BaseStringFacts | Truthy,
BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull,
BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy,
NumberFacts = BaseNumberFacts | Truthy,
@@ -407,7 +410,7 @@ namespace ts {
ZeroFacts = BaseNumberFacts,
NonZeroStrictFacts = BaseNumberStrictFacts | Truthy,
NonZeroFacts = BaseNumberFacts | Truthy,
BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull,
BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy,
BooleanFacts = BaseBooleanFacts | Truthy,
@@ -415,14 +418,15 @@ namespace ts {
FalseFacts = BaseBooleanFacts,
TrueStrictFacts = BaseBooleanStrictFacts | Truthy,
TrueFacts = BaseBooleanFacts | Truthy,
SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull | Truthy,
SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEOptional | NEUndefinedOrNull | Truthy | Discriminatable,
ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull | Truthy | Discriminatable,
FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | NEOptional | Falsy,
OptionalFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQOptional | EQUndefinedOrNull | NENull | Falsy,
NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | NEOptional | Falsy,
}
const typeofEQFacts = createMapFromTemplate({
@@ -10149,6 +10153,10 @@ namespace ts {
return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
}
function getNonOptionalType(type: Type): Type {
return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEOptional) : type;
}
/**
* Return true if type was inferred from an object literal or written as an object type literal
* with no call or construct signatures.
@@ -10235,6 +10243,9 @@ namespace ts {
function getWidenedType(type: Type): Type {
if (type.flags & TypeFlags.RequiresWidening) {
if (type.flags & TypeFlags.Optional) {
return strictNullChecks ? undefinedType : anyType;
}
if (type.flags & TypeFlags.Nullable) {
return anyType;
}
@@ -11098,7 +11109,7 @@ namespace ts {
strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
}
if (flags & (TypeFlags.Void | TypeFlags.Undefined)) {
return TypeFacts.UndefinedFacts;
return flags & TypeFlags.Optional ? TypeFacts.OptionalFacts : TypeFacts.UndefinedFacts;
}
if (flags & TypeFlags.Null) {
return TypeFacts.NullFacts;
@@ -14697,7 +14708,14 @@ namespace ts {
return checkNonNullType(checkExpression(node), node);
}
function checkNonNullType(type: Type, errorNode: Node): Type {
function checkNonNullType(type: Type, errorNode: Node, optionality?: NodeFlags): Type {
if (optionality & NodeFlags.OptionalExpression) {
type = getNonNullableType(type);
}
else if (optionality & NodeFlags.OptionalChain) {
type = getNonOptionalType(type);
}
const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;
if (kind) {
error(errorNode, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
@@ -14718,8 +14736,38 @@ namespace ts {
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
}
// function checkOptionality(node: Node) {
// if (node.flags & NodeFlags.OptionalExpression) {
// if (!compilerOptions.experimentalOptionalChaining) {
// error(node, Diagnostics.Experimental_support_for_optional_chaining_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalOptionalChaining_option_to_remove_this_warning);
// }
// return strictNullChecks;
// }
// return false;
// }
function propagateOptionalChain(type: Type, sourceType: Type, optionality: NodeFlags) {
const expectedFacts = optionality & NodeFlags.OptionalExpression ? TypeFacts.EQUndefinedOrNull : TypeFacts.EQOptional;
return optionality && strictNullChecks && getTypeFacts(sourceType) & expectedFacts
? getUnionType([type, optionalType])
: type;
}
function propagateOptionalChainSignature(signature: Signature, sourceType: Type, optionality: NodeFlags) {
const expectedFacts = optionality & NodeFlags.OptionalExpression ? TypeFacts.EQUndefinedOrNull : TypeFacts.EQOptional;
if (optionality && strictNullChecks && getTypeFacts(sourceType) & expectedFacts) {
signature = cloneSignature(signature);
signature.resolvedReturnType = getUnionType([getReturnTypeOfSignature(signature), optionalType]);
}
return signature;
}
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
const type = checkNonNullExpression(left);
// if a node is an OptionalExpression, use its non-null type
const optionality = node.flags & NodeFlags.Optional;
const sourceType = checkExpression(left);
const type = checkNonNullType(sourceType, left, optionality);
if (isTypeAny(type) || type === silentNeverType) {
return type;
}
@@ -14768,10 +14816,11 @@ namespace ts {
if (node.kind !== SyntaxKind.PropertyAccessExpression || assignmentKind === AssignmentKind.Definite ||
!(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
return propType;
return propagateOptionalChain(propType, sourceType, optionality);
}
const flowType = getFlowTypeOfReference(node, propType);
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
const resultType = assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
return propagateOptionalChain(resultType, sourceType, optionality);
}
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
@@ -15040,7 +15089,9 @@ namespace ts {
}
function checkIndexedAccess(node: ElementAccessExpression): Type {
const objectType = checkNonNullExpression(node.expression);
const optionality = node.flags & NodeFlags.Optional;
const sourceType = checkExpression(node.expression);
const objectType = checkNonNullType(sourceType, node.expression, optionality);
const indexExpression = node.argumentExpression;
if (!indexExpression) {
@@ -15069,7 +15120,8 @@ namespace ts {
return unknownType;
}
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
const resultType = checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
return propagateOptionalChain(resultType, objectType, optionality);
}
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
@@ -16131,7 +16183,9 @@ namespace ts {
return resolveUntypedCall(node);
}
const funcType = checkNonNullExpression(node.expression);
const optionality = node.flags & NodeFlags.Optional;
const sourceType = checkExpression(node.expression);
const funcType = checkNonNullType(sourceType, node.expression, optionality);
if (funcType === silentNeverType) {
return silentNeverSignature;
}
@@ -16172,7 +16226,9 @@ namespace ts {
}
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray);
const signature = resolveCall(node, callSignatures, candidatesOutArray);
return propagateOptionalChainSignature(signature, sourceType, optionality);
}
/**
+6
View File
@@ -433,6 +433,12 @@ namespace ts {
category: Diagnostics.Experimental_Options,
description: Diagnostics.Enables_experimental_support_for_emitting_type_metadata_for_decorators
},
{
name: "experimentalOptionalChaining",
type: "boolean",
category: Diagnostics.Experimental_Options,
description: Diagnostics.Enables_experimental_support_for_optional_chaining
},
// Advanced
{
+9
View File
@@ -831,6 +831,11 @@
"category": "Error",
"code": 1254
},
"Experimental support for optional chaining is a feature that is subject to change in a future release. Set the 'experimentalOptionalChaining' option to remove this warning.": {
"category": "Error",
"code": 1255
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300
@@ -3314,6 +3319,10 @@
"category": "Message",
"code": 6185
},
"Enables experimental support for optional chaining.": {
"category": "Message",
"code": 6186
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
+8 -4
View File
@@ -1195,12 +1195,13 @@ namespace ts {
}
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
const isOptionalExpression = node.flags & NodeFlags.OptionalExpression;
let indentBeforeDot = false;
let indentAfterDot = false;
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
const dotRangeStart = node.expression.end;
const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1;
const dotToken = createToken(SyntaxKind.DotToken);
const dotToken = createToken(isOptionalExpression ? SyntaxKind.QuestionDotToken : SyntaxKind.DotToken);
dotToken.pos = dotRangeStart;
dotToken.end = dotRangeEnd;
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
@@ -1210,8 +1211,8 @@ namespace ts {
emitExpression(node.expression);
increaseIndentIf(indentBeforeDot);
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
write(shouldEmitDotDot ? ".." : ".");
const shouldEmitDotDot = !isOptionalExpression && !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
write(shouldEmitDotDot ? ".." : isOptionalExpression ? "?." : ".");
increaseIndentIf(indentAfterDot);
emit(node.name);
@@ -1240,13 +1241,16 @@ namespace ts {
function emitElementAccessExpression(node: ElementAccessExpression) {
emitExpression(node.expression);
write("[");
write(node.flags & NodeFlags.OptionalExpression ? "?.[" : "[");
emitExpression(node.argumentExpression);
write("]");
}
function emitCallExpression(node: CallExpression) {
emitExpression(node.expression);
if (node.flags & NodeFlags.OptionalExpression) {
write("?.");
}
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
}
+7
View File
@@ -859,6 +859,7 @@ namespace ts {
export function createPropertyAccess(expression: Expression, name: string | Identifier) {
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
if (expression.flags & NodeFlags.Optional) node.flags |= NodeFlags.OptionalChain;
node.expression = parenthesizeForAccess(expression);
node.name = asName(name);
setEmitFlags(node, EmitFlags.NoIndentation);
@@ -876,6 +877,7 @@ namespace ts {
export function createElementAccess(expression: Expression, index: number | Expression) {
const node = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression);
if (expression.flags & NodeFlags.Optional) node.flags |= NodeFlags.OptionalChain;
node.expression = parenthesizeForAccess(expression);
node.argumentExpression = asExpression(index);
return node;
@@ -890,6 +892,7 @@ namespace ts {
export function createCall(expression: Expression, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentsArray: ReadonlyArray<Expression>) {
const node = <CallExpression>createSynthesizedNode(SyntaxKind.CallExpression);
if (expression.flags & NodeFlags.Optional) node.flags |= NodeFlags.OptionalChain;
node.expression = parenthesizeForAccess(expression);
node.typeArguments = asNodeArray(typeArguments);
node.arguments = parenthesizeListElements(createNodeArray(argumentsArray));
@@ -2502,6 +2505,10 @@ namespace ts {
return createBinary(left, SyntaxKind.EqualsToken, right);
}
export function createEquality(left: Expression, right: Expression) {
return createBinary(left, SyntaxKind.EqualsEqualsToken, right);
}
export function createStrictEquality(left: Expression, right: Expression) {
return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right);
}
+57 -24
View File
@@ -2532,6 +2532,11 @@ namespace ts {
return token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken;
}
function nextTokenIsOpenBracket() {
nextToken();
return token() === SyntaxKind.OpenBracketToken;
}
function parseTypeLiteral(): TypeLiteralNode {
const node = <TypeLiteralNode>createNode(SyntaxKind.TypeLiteral);
node.members = parseObjectTypeMembers();
@@ -4186,9 +4191,22 @@ namespace ts {
function parseMemberExpressionRest(expression: LeftHandSideExpression): MemberExpression {
while (true) {
const dotToken = parseOptionalToken(SyntaxKind.DotToken);
if (dotToken) {
const isOptionalExpression = token() === SyntaxKind.QuestionDotToken;
const isOptionalCall = isOptionalExpression && lookAhead(nextTokenIsOpenParenOrLessThan);
if (isOptionalCall) {
// In an optional-chaining call or new expression, we defer parsing `.?` to parseCallExpressionRest.
return <MemberExpression>expression;
}
let flags: NodeFlags = NodeFlags.None;
if (isOptionalExpression) flags |= NodeFlags.OptionalExpression;
if (expression.flags & NodeFlags.Optional) flags |= NodeFlags.OptionalChain;
const isOptionalPropertyAccess = isOptionalExpression && !lookAhead(nextTokenIsOpenBracket);
if (isOptionalPropertyAccess || token() === SyntaxKind.DotToken) {
nextToken();
const propertyAccess = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, expression.pos);
propertyAccess.flags = flags;
propertyAccess.expression = expression;
propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true);
expression = finishNode(propertyAccess);
@@ -4198,14 +4216,18 @@ namespace ts {
if (token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
nextToken();
const nonNullExpression = <NonNullExpression>createNode(SyntaxKind.NonNullExpression, expression.pos);
nonNullExpression.flags = flags;
nonNullExpression.expression = expression;
expression = finishNode(nonNullExpression);
continue;
}
// when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName
if (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken)) {
// however, `?.[` is unambiguously *not* a ComputedPropertyName.
const isOptionalElementAccess = isOptionalExpression && !isOptionalPropertyAccess;
if (isOptionalElementAccess || (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken))) {
const indexedAccess = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression, expression.pos);
indexedAccess.flags = flags;
indexedAccess.expression = expression;
// It's not uncommon for a user to write: "new Type[]".
@@ -4225,6 +4247,7 @@ namespace ts {
if (token() === SyntaxKind.NoSubstitutionTemplateLiteral || token() === SyntaxKind.TemplateHead) {
const tagExpression = <TaggedTemplateExpression>createNode(SyntaxKind.TaggedTemplateExpression, expression.pos);
tagExpression.flags = flags;
tagExpression.tag = expression;
tagExpression.template = token() === SyntaxKind.NoSubstitutionTemplateLiteral
? <NoSubstitutionTemplateLiteral>parseLiteralNode()
@@ -4240,32 +4263,41 @@ namespace ts {
function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression {
while (true) {
expression = parseMemberExpressionRest(expression);
// If we parsed MemberExpression and see a `?.` here, then we are part of an optional chain.
const isOptionalExpression = parseOptional(SyntaxKind.QuestionDotToken);
let flags: NodeFlags = NodeFlags.None;
if (isOptionalExpression) flags |= NodeFlags.OptionalExpression;
if (expression.flags & NodeFlags.Optional) flags |= NodeFlags.OptionalChain;
let typeArguments: NodeArray<TypeNode>;
if (token() === SyntaxKind.LessThanToken) {
// See if this is the start of a generic invocation. If so, consume it and
// keep checking for postfix expressions. Otherwise, it's just a '<' that's
// part of an arithmetic expression. Break out so we consume it higher in the
// stack.
const typeArguments = tryParse(parseTypeArgumentsInExpression);
if (!typeArguments) {
return expression;
if (isOptionalExpression) {
// If we are part of an optional chain, this *must* be a generic call.
typeArguments = parseTypeArgumentsInExpression();
}
else {
// See if this is the start of a generic invocation. If so, consume it and
// keep checking for postfix expressions. Otherwise, it's just a '<' that's
// part of an arithmetic expression. Break out so we consume it higher in the
// stack.
typeArguments = tryParse(parseTypeArgumentsInExpression);
if (!typeArguments) {
return expression;
}
}
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
callExpr.expression = expression;
callExpr.typeArguments = typeArguments;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
continue;
}
else if (token() === SyntaxKind.OpenParenToken) {
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
callExpr.expression = expression;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
continue;
else if (token() !== SyntaxKind.LessThanToken) {
return expression;
}
return expression;
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
callExpr.flags = flags;
callExpr.expression = expression;
callExpr.typeArguments = typeArguments;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
}
}
@@ -4306,6 +4338,7 @@ namespace ts {
case SyntaxKind.ColonToken: // foo<x>:
case SyntaxKind.SemicolonToken: // foo<x>;
case SyntaxKind.QuestionToken: // foo<x>?
case SyntaxKind.QuestionDotToken: // foo<x>?.
case SyntaxKind.EqualsEqualsToken: // foo<x> ==
case SyntaxKind.EqualsEqualsEqualsToken: // foo<x> ===
case SyntaxKind.ExclamationEqualsToken: // foo<x> !=
+4
View File
@@ -169,6 +169,7 @@ namespace ts {
"&&": SyntaxKind.AmpersandAmpersandToken,
"||": SyntaxKind.BarBarToken,
"?": SyntaxKind.QuestionToken,
"?.": SyntaxKind.QuestionDotToken,
":": SyntaxKind.ColonToken,
"=": SyntaxKind.EqualsToken,
"+=": SyntaxKind.PlusEqualsToken,
@@ -1553,6 +1554,9 @@ namespace ts {
pos++;
return token = SyntaxKind.GreaterThanToken;
case CharacterCodes.question:
if (text.charCodeAt(pos + 1) === CharacterCodes.dot && !isDigit(text.charCodeAt(pos + 2))) {
return pos += 2, token = SyntaxKind.QuestionDotToken;
}
pos++;
return token = SyntaxKind.QuestionToken;
case CharacterCodes.openBracket:
+103
View File
@@ -26,6 +26,7 @@ namespace ts {
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
let optionalExpression: PropertyAccessExpression | ElementAccessExpression | CallExpression | undefined;
let enabledSubstitutions: ESNextSubstitutionFlags;
let enclosingFunctionFlags: FunctionFlags;
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
@@ -39,6 +40,7 @@ namespace ts {
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
optionalExpression = undefined;
return visited;
}
@@ -103,6 +105,12 @@ namespace ts {
return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue);
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
case SyntaxKind.CallExpression:
return visitCallExpression(node as CallExpression);
case SyntaxKind.PropertyAccessExpression:
return visitPropertyAccessExpression(node as PropertyAccessExpression);
case SyntaxKind.ElementAccessExpression:
return visitElementAccessExpression(node as ElementAccessExpression);
default:
return visitEachChild(node, visitor, context);
}
@@ -718,6 +726,101 @@ namespace ts {
return statements;
}
function getEffectiveExpressionOfOptionalExpression(node: PropertyAccessExpression | ElementAccessExpression | CallExpression): Expression | undefined {
if (node.flags & NodeFlags.OptionalExpression) {
return node.expression;
}
if (node.flags & NodeFlags.OptionalChain) {
if (isPropertyAccessExpression(node.expression) ||
isElementAccessExpression(node.expression) ||
isCallExpression(node.expression)) {
return getEffectiveExpressionOfOptionalExpression(node.expression);
}
}
}
type OptionalChain = PropertyAccessExpression | ElementAccessExpression | CallExpression;
function isOptionalExpression(node: OptionalChain) {
return !!(node.flags & NodeFlags.OptionalExpression);
}
function isOptionalChain(node: Expression): node is OptionalChain {
return isPropertyAccessExpression(node)
|| isElementAccessExpression(node)
|| isCallExpression(node);
}
function visitOptionalChain(node: OptionalChain) {
let chain = node;
const stack: OptionalChain[] = [chain];
while (!isOptionalExpression(chain) && isOptionalChain(chain.expression)) {
chain = chain.expression;
stack.push(chain);
}
const temp = createTempVariable(hoistVariableDeclaration);
const root = visitNode(chain.expression, visitor, isExpression);
setOriginalNode(temp, root);
setSourceMapRange(temp, root);
setEmitFlags(temp, EmitFlags.NoComments);
let expression: LeftHandSideExpression = temp;
while (stack.length) {
chain = stack.pop();
switch (chain.kind) {
case SyntaxKind.PropertyAccessExpression:
expression = createPropertyAccess(expression, visitNode(chain.name, visitor, isIdentifier));
break;
case SyntaxKind.ElementAccessExpression:
expression = createElementAccess(expression, visitNode(chain.argumentExpression, visitor, isExpression));
break;
case SyntaxKind.CallExpression:
expression = createCall(expression, /*typeArguments*/ undefined, visitNodes(chain.arguments, visitor, isExpression));
break;
}
setOriginalNode(expression, chain);
setSourceMapRange(expression, chain);
setCommentRange(expression, chain);
setEmitFlags(expression, EmitFlags.NoLeadingComments);
}
const condition = createEquality(createAssignment(temp, root), createNull());
setSourceMapRange(condition, root);
const voidZero = createVoidZero();
setSourceMapRange(voidZero, root);
const conditional = createConditional(condition, voidZero, expression);
setOriginalNode(conditional, node);
setSourceMapRange(conditional, node);
setCommentRange(conditional, node);
return conditional;
}
function visitPropertyAccessExpression(node: PropertyAccessExpression) {
if (node.flags & NodeFlags.Optional) {
return visitOptionalChain(node);
}
return visitEachChild(node, visitor, context);
}
function visitElementAccessExpression(node: ElementAccessExpression) {
if (node.flags & NodeFlags.Optional) {
return visitOptionalChain(node);
}
return visitEachChild(node, visitor, context);
}
function visitCallExpression(node: CallExpression) {
if (node.flags & NodeFlags.Optional) {
return visitOptionalChain(node);
}
return visitEachChild(node, visitor, context);
}
function enableSubstitutionForAsyncMethodsWithSuper() {
if ((enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper) === 0) {
enabledSubstitutions |= ESNextSubstitutionFlags.AsyncMethodsWithSuper;
+28 -20
View File
@@ -81,6 +81,7 @@ namespace ts {
DotDotDotToken,
SemicolonToken,
CommaToken,
QuestionDotToken,
LessThanToken,
LessThanSlashToken,
GreaterThanToken,
@@ -382,6 +383,7 @@ namespace ts {
CommaListExpression,
MergeDeclarationMarker,
EndOfDeclarationMarker,
OptionalExpression,
// Enum value count
Count,
@@ -425,20 +427,22 @@ namespace ts {
NestedNamespace = 1 << 2, // Namespace declaration
Synthesized = 1 << 3, // Node was synthesized during transformation
Namespace = 1 << 4, // Namespace declaration
ExportContext = 1 << 5, // Export context (initialized by binding)
ContainsThis = 1 << 6, // Interface contains references to "this"
HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope
HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding)
DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed
YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator
DecoratorContext = 1 << 13, // If node was parsed as part of a decorator
AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function
ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node
JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript
ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error
HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node
OptionalExpression = 1 << 5, // OptionalExpression (?.)
OptionalChain = 1 << 6, // Optional chaining (expressions following ?.)
ExportContext = 1 << 7, // Export context (initialized by binding)
ContainsThis = 1 << 8, // Interface contains references to "this"
HasImplicitReturn = 1 << 9, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 10, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 11, // Set if module declaration is an augmentation for the global scope
HasAsyncFunctions = 1 << 12, // If the file has async functions (initialized by binding)
DisallowInContext = 1 << 13, // If node was parsed in a context where 'in-expressions' are not allowed
YieldContext = 1 << 14, // If node was parsed in the 'yield' context created when parsing a generator
DecoratorContext = 1 << 15, // If node was parsed as part of a decorator
AwaitContext = 1 << 16, // If node was parsed in the 'await' context created when parsing an async function
ThisNodeHasError = 1 << 17, // If the parser encountered an error when parsing the code that created this node
JavaScriptFile = 1 << 18, // If node was parsed in a JavaScript
ThisNodeOrAnySubNodesHasError = 1 << 19, // If this node or any of its children had an error
HasAggregatedChildData = 1 << 20, // If we've computed data from children and cached it in this node
// This flag will be set when the parser encounters a dynamic import expression so that module resolution
// will not have to walk the tree if the flag is not set. However, this flag is just a approximation because
@@ -449,8 +453,8 @@ namespace ts {
// The advantage of this approach is its simplicity. For the case of batch compilation,
// we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used.
/* @internal */
PossiblyContainsDynamicImport = 1 << 19,
JSDoc = 1 << 20, // If node was parsed inside jsdoc
PossiblyContainsDynamicImport = 1 << 20,
JSDoc = 1 << 21, // If node was parsed inside jsdoc
BlockScoped = Let | Const,
@@ -458,10 +462,12 @@ namespace ts {
ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | OptionalChain,
// Exclude these flags when parsing a Type
TypeExcludesFlags = YieldContext | AwaitContext,
Optional = OptionalExpression | OptionalChain,
}
export const enum ModifierFlags {
@@ -3215,17 +3221,18 @@ namespace ts {
NonPrimitive = 1 << 24, // intrinsic object type
/* @internal */
JsxAttributes = 1 << 25, // Jsx attributes type
/* @internal */
Optional = 1 << 26, // Optional chaining type marker
Nullable = Undefined | Null,
Literal = StringLiteral | NumberLiteral | BooleanLiteral,
Unit = Literal | Nullable,
StringOrNumberLiteral = StringLiteral | NumberLiteral,
/* @internal */
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null | Optional,
PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean,
/* @internal */
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive | Optional,
/* @internal */
Primitive = String | Number | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal,
StringLike = String | StringLiteral | Index,
@@ -3656,6 +3663,7 @@ namespace ts {
emitBOM?: boolean;
emitDecoratorMetadata?: boolean;
experimentalDecorators?: boolean;
experimentalOptionalChaining?: boolean;
forceConsistentCasingInFileNames?: boolean;
/*@internal*/help?: boolean;
importHelpers?: boolean;