parse, emit, initial check for optional chaining

This commit is contained in:
Ron Buckton
2017-09-27 14:10:09 -04:00
parent 650fd3870c
commit 625dc0f32e
10 changed files with 465 additions and 318 deletions
+19 -14
View File
@@ -2674,11 +2674,24 @@ namespace ts {
case SyntaxKind.PropertyAccessExpression:
return computePropertyAccess(<PropertyAccessExpression>node, subtreeFlags);
case SyntaxKind.CallChain:
return computeCallChain(<CallChain>node, subtreeFlags);
default:
return computeOther(node, kind, subtreeFlags);
}
}
function computeCallChain(node: CallChain, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
if (node.typeArguments) {
transformFlags |= TransformFlags.AssertTypeScript;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes;
}
function computeCallExpression(node: CallExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const expression = node.expression;
@@ -2688,10 +2701,6 @@ 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
@@ -3136,10 +3145,6 @@ 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) {
@@ -3296,6 +3301,12 @@ namespace ts {
transformFlags |= TransformFlags.AssertJsx;
break;
case SyntaxKind.OptionalExpression:
case SyntaxKind.PropertyAccessChain:
case SyntaxKind.ElementAccessChain:
transformFlags |= TransformFlags.AssertESNext;
break;
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.TemplateHead:
case SyntaxKind.TemplateMiddle:
@@ -3466,12 +3477,6 @@ namespace ts {
break;
case SyntaxKind.ElementAccessExpression:
if (node.flags & NodeFlags.Optional) {
transformFlags |= TransformFlags.AssertESNext;
}
break;
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForStatement:
+93 -111
View File
@@ -251,7 +251,6 @@ 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");
@@ -380,21 +379,19 @@ namespace ts {
TypeofNEFunction = 1 << 12, // typeof x !== "function"
TypeofNEHostObject = 1 << 13, // typeof x !== "xxx"
EQUndefined = 1 << 14, // x === undefined
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,
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,
// 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 | NEOptional | NEUndefinedOrNull,
BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy,
StringFacts = BaseStringFacts | Truthy,
@@ -402,7 +399,7 @@ namespace ts {
EmptyStringFacts = BaseStringFacts,
NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy,
NonEmptyStringFacts = BaseStringFacts | Truthy,
BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull,
BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy,
NumberFacts = BaseNumberFacts | Truthy,
@@ -410,7 +407,7 @@ namespace ts {
ZeroFacts = BaseNumberFacts,
NonZeroStrictFacts = BaseNumberStrictFacts | Truthy,
NonZeroFacts = BaseNumberFacts | Truthy,
BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull,
BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy,
BooleanFacts = BaseBooleanFacts | Truthy,
@@ -418,15 +415,14 @@ namespace ts {
FalseFacts = BaseBooleanFacts,
TrueStrictFacts = BaseBooleanStrictFacts | Truthy,
TrueFacts = BaseBooleanFacts | Truthy,
SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull | Truthy,
SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEOptional | NEUndefinedOrNull | Truthy | Discriminatable,
ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEOptional | NEUndefinedOrNull | Truthy | Discriminatable,
FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | 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,
UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
}
const typeofEQFacts = createMapFromTemplate({
@@ -7613,7 +7609,7 @@ namespace ts {
return type;
}
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode, cacheSymbol: boolean) {
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | ElementAccessChain | IndexedAccessTypeNode, cacheSymbol: boolean) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
const propName = indexType.flags & TypeFlags.StringOrNumberLiteral ?
escapeLeadingUnderscores("" + (<LiteralType>indexType).value) :
@@ -7661,7 +7657,7 @@ namespace ts {
}
}
if (accessNode) {
const indexNode = accessNode.kind === SyntaxKind.ElementAccessExpression ? (<ElementAccessExpression>accessNode).argumentExpression : (<IndexedAccessTypeNode>accessNode).indexType;
const indexNode = accessNode.kind === SyntaxKind.IndexedAccessType ? (<IndexedAccessTypeNode>accessNode).indexType : (<ElementAccessExpression | ElementAccessChain>accessNode).argumentExpression;
if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, "" + (<LiteralType>indexType).value, typeToString(objectType));
}
@@ -7737,13 +7733,14 @@ namespace ts {
return undefined;
}
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | ElementAccessChain | IndexedAccessTypeNode): Type {
// If the index type is generic, or if the object type is generic and doesn't originate in an expression,
// we are performing a higher-order index access where we cannot meaningfully access the properties of the
// object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates in
// an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]'
// has always been resolved eagerly using the constraint type of 'this' at the given location.
if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) {
if (isGenericIndexType(indexType) || !(accessNode && (accessNode.kind === SyntaxKind.ElementAccessExpression || accessNode.kind === SyntaxKind.ElementAccessChain)) &&
isGenericObjectType(objectType)) {
if (objectType.flags & TypeFlags.Any) {
return objectType;
}
@@ -10153,10 +10150,6 @@ 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.
@@ -10243,9 +10236,6 @@ 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;
}
@@ -11109,7 +11099,7 @@ namespace ts {
strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
}
if (flags & (TypeFlags.Void | TypeFlags.Undefined)) {
return flags & TypeFlags.Optional ? TypeFacts.OptionalFacts : TypeFacts.UndefinedFacts;
return TypeFacts.UndefinedFacts;
}
if (flags & TypeFlags.Null) {
return TypeFacts.NullFacts;
@@ -14609,14 +14599,14 @@ namespace ts {
* Check whether the requested property access is valid.
* Returns true if node is a valid property access, and false otherwise.
* @param node The node to be checked.
* @param left The left hand side of the property access (e.g.: the super in `super.foo`).
* @param isSuperProperty Whether the left hand side of the property access is `super`.
* @param type The type of left.
* @param prop The symbol for the right hand side of the property access.
*/
function checkPropertyAccessibility(node: PropertyAccessExpression | QualifiedName | VariableLikeDeclaration, left: Expression | QualifiedName, type: Type, prop: Symbol): boolean {
function checkPropertyAccessibility(node: PropertyAccessExpression | PropertyAccessChain | QualifiedName | VariableLikeDeclaration, isSuperProperty: boolean, type: Type, prop: Symbol): boolean {
const flags = getDeclarationModifierFlagsFromSymbol(prop);
const errorNode = node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.VariableDeclaration ?
(<PropertyAccessExpression | VariableDeclaration>node).name :
const errorNode = node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.PropertyAccessChain ?
(<PropertyAccessExpression | PropertyAccessChain | VariableDeclaration>node).name :
(<QualifiedName>node).right;
if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) {
@@ -14625,7 +14615,7 @@ namespace ts {
return false;
}
if (left.kind === SyntaxKind.SuperKeyword) {
if (isSuperProperty) {
// TS 1.0 spec (April 2014): 4.8.2
// - In a constructor, instance member function, instance member accessor, or
// instance member variable initializer where this references a derived class instance,
@@ -14673,7 +14663,7 @@ namespace ts {
// Property is known to be protected at this point
// All protected properties of a supertype are accessible in a super access
if (left.kind === SyntaxKind.SuperKeyword) {
if (isSuperProperty) {
return true;
}
@@ -14708,14 +14698,7 @@ namespace ts {
return checkNonNullType(checkExpression(node), node);
}
function checkNonNullType(type: Type, errorNode: Node, optionality?: NodeFlags): Type {
if (optionality & NodeFlags.OptionalExpression) {
type = getNonNullableType(type);
}
else if (optionality & NodeFlags.OptionalChain) {
type = getNonOptionalType(type);
}
function checkNonNullType(type: Type, errorNode: Node): Type {
const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;
if (kind) {
error(errorNode, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
@@ -14729,51 +14712,20 @@ namespace ts {
}
function checkPropertyAccessExpression(node: PropertyAccessExpression) {
return checkPropertyAccessExpressionOrQualifiedName(node, node.expression, node.name);
return checkPropertyAccessExpressionOrQualifiedName(node, checkNonNullExpression(node.expression), node.name, node.expression.kind === SyntaxKind.SuperKeyword);
}
function checkQualifiedName(node: QualifiedName) {
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
return checkPropertyAccessExpressionOrQualifiedName(node, checkNonNullExpression(node.left), node.right, /*isSuperProperty*/ false);
}
// 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) {
// 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;
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | PropertyAccessChain | QualifiedName, leftType: Type, right: Identifier, isSuperProperty: boolean) {
if (isTypeAny(leftType) || leftType === silentNeverType) {
return leftType;
}
const apparentType = getApparentType(getWidenedType(type));
if (apparentType === unknownType || (type.flags & TypeFlags.TypeParameter && isTypeAny(apparentType))) {
const apparentType = getApparentType(getWidenedType(leftType));
if (apparentType === unknownType || (leftType.flags & TypeFlags.TypeParameter && isTypeAny(apparentType))) {
// handle cases when type is Type parameter with invalid or any constraint
return apparentType;
}
@@ -14787,7 +14739,7 @@ namespace ts {
return indexInfo.type;
}
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type);
reportNonexistentProperty(right, leftType.flags & TypeFlags.TypeParameter && (leftType as TypeParameter).isThisType ? apparentType : leftType);
}
return unknownType;
}
@@ -14798,7 +14750,7 @@ namespace ts {
getNodeLinks(node).resolvedSymbol = prop;
checkPropertyAccessibility(node, left, apparentType, prop);
checkPropertyAccessibility(node, isSuperProperty, apparentType, prop);
const propType = getDeclaredOrApparentType(prop, node);
const assignmentKind = getAssignmentTargetKind(node);
@@ -14816,14 +14768,13 @@ 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 propagateOptionalChain(propType, sourceType, optionality);
return propType;
}
const flowType = getFlowTypeOfReference(node, propType);
const resultType = assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
return propagateOptionalChain(resultType, sourceType, optionality);
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
}
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | PropertyAccessChain | QualifiedName, right: Identifier): void {
const { valueDeclaration } = prop;
if (!valueDeclaration) {
return;
@@ -15021,7 +14972,7 @@ namespace ts {
if (type !== unknownType && !isTypeAny(type)) {
const prop = getPropertyOfType(type, propertyName);
if (prop) {
return checkPropertyAccessibility(node, left, type, prop);
return checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, type, prop);
}
// In js files properties of unions are allowed in completion
@@ -15089,10 +15040,11 @@ namespace ts {
}
function checkIndexedAccess(node: ElementAccessExpression): Type {
const optionality = node.flags & NodeFlags.Optional;
const sourceType = checkExpression(node.expression);
const objectType = checkNonNullType(sourceType, node.expression, optionality);
const objectType = checkNonNullExpression(node.expression);
return checkIndexedAccessWorker(node, objectType);
}
function checkIndexedAccessWorker(node: ElementAccessExpression | ElementAccessChain, objectType: Type) {
const indexExpression = node.argumentExpression;
if (!indexExpression) {
const sourceFile = getSourceFileOfNode(node);
@@ -15120,8 +15072,7 @@ namespace ts {
return unknownType;
}
const resultType = checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
return propagateOptionalChain(resultType, objectType, optionality);
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
}
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
@@ -15166,6 +15117,34 @@ namespace ts {
return true;
}
function getLeftTypeOfOptionalChain(node: OptionalChain) {
if (node.chain) return checkOptionalChain(node.chain);
const optionalExpression = findAncestor(node, isOptionalExpression);
const type = checkExpressionCached(optionalExpression.expression);
return getNonNullableType(type);
}
function checkOptionalChain(node: OptionalChain): Type {
const objectType = getLeftTypeOfOptionalChain(node);
switch (node.kind) {
case SyntaxKind.PropertyAccessChain:
return checkPropertyAccessExpressionOrQualifiedName(node, objectType, node.name, /*isSuperProperty*/ false);
case SyntaxKind.ElementAccessChain:
return checkIndexedAccessWorker(node, objectType);
case SyntaxKind.CallChain:
return checkCallExpression(node);
}
}
function checkOptionalExpression(node: OptionalExpression) {
const type = checkExpression(node.expression);
const chainType = checkOptionalChain(node.chain);
if ((strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable) {
return getNullableType(chainType, TypeFlags.Undefined);
}
return chainType;
}
function callLikeExpressionMayHaveTypeArguments(node: CallLikeExpression): node is CallExpression | NewExpression {
// TODO: Also include tagged templates (https://github.com/Microsoft/TypeScript/issues/11947)
return isCallOrNewExpression(node);
@@ -15383,7 +15362,10 @@ namespace ts {
// example, given a 'function wrap<T, U>(cb: (x: T) => U): (x: T) => U' and a call expression
// 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the
// return type of 'wrap'.
if (node.kind !== SyntaxKind.Decorator) {
if (node.kind === SyntaxKind.CallChain) {
sys.write("TODO: getContextualTypeOfCallChain");
}
else if (node.kind !== SyntaxKind.Decorator) {
const contextualType = getContextualType(node);
if (contextualType) {
// We clone the contextual mapper to avoid disturbing a resolution in progress for an
@@ -16168,8 +16150,8 @@ namespace ts {
return maxParamsIndex;
}
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
function resolveCallExpression(node: CallExpression | CallChain, candidatesOutArray: Signature[]): Signature {
if (node.kind !== SyntaxKind.CallChain && node.expression.kind === SyntaxKind.SuperKeyword) {
const superType = checkSuperExpression(node.expression);
if (superType !== unknownType) {
// In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated
@@ -16183,9 +16165,7 @@ namespace ts {
return resolveUntypedCall(node);
}
const optionality = node.flags & NodeFlags.Optional;
const sourceType = checkExpression(node.expression);
const funcType = checkNonNullType(sourceType, node.expression, optionality);
const funcType = node.kind === SyntaxKind.CallChain ? getLeftTypeOfOptionalChain(node) : checkNonNullExpression(node.expression);
if (funcType === silentNeverType) {
return silentNeverSignature;
}
@@ -16226,9 +16206,7 @@ namespace ts {
}
return resolveErrorCall(node);
}
const signature = resolveCall(node, callSignatures, candidatesOutArray);
return propagateOptionalChainSignature(signature, sourceType, optionality);
return resolveCall(node, callSignatures, candidatesOutArray);
}
/**
@@ -16495,7 +16473,8 @@ namespace ts {
function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
switch (node.kind) {
case SyntaxKind.CallExpression:
return resolveCallExpression(<CallExpression>node, candidatesOutArray);
case SyntaxKind.CallChain:
return resolveCallExpression(node, candidatesOutArray);
case SyntaxKind.NewExpression:
return resolveNewExpression(<NewExpression>node, candidatesOutArray);
case SyntaxKind.TaggedTemplateExpression:
@@ -16590,13 +16569,14 @@ namespace ts {
* @param node The call/new expression to be checked.
* @returns On success, the expression's signature's return type. On failure, anyType.
*/
function checkCallExpression(node: CallExpression | NewExpression): Type {
function checkCallExpression(node: CallExpression | CallChain | NewExpression): Type {
// Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node.arguments);
const signature = getResolvedSignature(node);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const isSuper = node.kind !== SyntaxKind.CallChain && node.expression.kind === SyntaxKind.SuperKeyword;
if (isSuper) {
return voidType;
}
@@ -18225,6 +18205,8 @@ namespace ts {
return checkPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return checkIndexedAccess(<ElementAccessExpression>node);
case SyntaxKind.OptionalExpression:
return checkOptionalExpression(<OptionalExpression>node);
case SyntaxKind.CallExpression:
if ((<CallExpression>node).expression.kind === SyntaxKind.ImportKeyword) {
return checkImportCallExpression(<ImportCall>node);
@@ -18973,7 +18955,7 @@ namespace ts {
forEach(node.types, checkSourceElement);
}
function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) {
function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | ElementAccessChain | IndexedAccessTypeNode) {
if (!(type.flags & TypeFlags.IndexedAccess)) {
return type;
}
@@ -20436,7 +20418,7 @@ namespace ts {
const property = getPropertyOfType(parentType, getTextOfPropertyName(name));
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined); // A destructuring is never a write-only reference.
if (parent.initializer && property) {
checkPropertyAccessibility(parent, parent.initializer, parentType, property);
checkPropertyAccessibility(parent, /*isSuperProperty*/ false, parentType, property);
}
}
+55 -7
View File
@@ -583,6 +583,14 @@ namespace ts {
case SyntaxKind.BindingElement:
return emitBindingElement(<BindingElement>node);
// Optional chains
case SyntaxKind.PropertyAccessChain:
return emitPropertyAccessChain(<PropertyAccessChain>node);
case SyntaxKind.ElementAccessChain:
return emitElementAccessChain(<ElementAccessChain>node);
case SyntaxKind.CallChain:
return emitCallChain(<CallChain>node);
// Misc
case SyntaxKind.TemplateSpan:
return emitTemplateSpan(<TemplateSpan>node);
@@ -768,6 +776,8 @@ namespace ts {
return emitPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return emitElementAccessExpression(<ElementAccessExpression>node);
case SyntaxKind.OptionalExpression:
return emitOptionalExpression(<OptionalExpression>node);
case SyntaxKind.CallExpression:
return emitCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
@@ -1195,13 +1205,12 @@ 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(isOptionalExpression ? SyntaxKind.QuestionDotToken : SyntaxKind.DotToken);
const dotToken = createToken(SyntaxKind.DotToken);
dotToken.pos = dotRangeStart;
dotToken.end = dotRangeEnd;
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
@@ -1211,8 +1220,8 @@ namespace ts {
emitExpression(node.expression);
increaseIndentIf(indentBeforeDot);
const shouldEmitDotDot = !isOptionalExpression && !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
write(shouldEmitDotDot ? ".." : isOptionalExpression ? "?." : ".");
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
write(shouldEmitDotDot ? ".." : ".");
increaseIndentIf(indentAfterDot);
emit(node.name);
@@ -1241,16 +1250,55 @@ namespace ts {
function emitElementAccessExpression(node: ElementAccessExpression) {
emitExpression(node.expression);
write(node.flags & NodeFlags.OptionalExpression ? "?.[" : "[");
write("[");
emitExpression(node.argumentExpression);
write("]");
}
function emitCallExpression(node: CallExpression) {
function emitOptionalExpression(node: OptionalExpression) {
emitExpression(node.expression);
if (node.flags & NodeFlags.OptionalExpression) {
emit(node.chain);
}
function emitPropertyAccessChain(node: PropertyAccessChain) {
if (node.chain) {
emit(node.chain);
write(".");
}
else {
write("?.");
}
emit(node.name);
}
function emitElementAccessChain(node: ElementAccessChain) {
if (node.chain) {
emit(node.chain);
}
else {
write("?.");
}
write("[");
emitExpression(node.argumentExpression);
write("]");
}
function emitCallChain(node: CallChain) {
if (node.chain) {
emit(node.chain);
}
else {
write("?.");
}
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
}
function emitCallExpression(node: CallExpression) {
emitExpression(node.expression);
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
}
+58 -7
View File
@@ -859,7 +859,6 @@ 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);
@@ -877,7 +876,6 @@ 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,9 +888,66 @@ namespace ts {
: node;
}
export function createOptionalExpression(expression: Expression, chain: OptionalChain) {
const node = <OptionalExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression);
node.expression = parenthesizeForAccess(expression);
node.chain = chain;
return node;
}
export function updateOptionalExpression(node: OptionalExpression, expression: Expression, chain: OptionalChain) {
return node.expression !== expression
|| node.chain !== chain
? updateNode(createOptionalExpression(expression, chain), node)
: node;
}
export function createPropertyAccessChain(chain: OptionalChain | undefined, name: Identifier) {
const node = <PropertyAccessChain>createSynthesizedNode(SyntaxKind.PropertyAccessChain);
node.chain = chain;
node.name = name;
return node;
}
export function updatePropertyAccessChain(node: PropertyAccessChain, chain: OptionalChain | undefined, name: Identifier) {
return node.chain !== chain
|| node.name !== name
? updateNode(createPropertyAccessChain(chain, name), node)
: node;
}
export function createElementAccessChain(chain: OptionalChain | undefined, argumentExpression: Expression) {
const node = <ElementAccessChain>createSynthesizedNode(SyntaxKind.ElementAccessChain);
node.chain = chain;
node.argumentExpression = argumentExpression;
return node;
}
export function updateElementAccessChain(node: ElementAccessChain, chain: OptionalChain | undefined, argumentExpression: Expression) {
return node.chain !== chain
|| node.argumentExpression !== argumentExpression
? updateNode(createElementAccessChain(chain, argumentExpression), node)
: node;
}
export function createCallChain(chain: OptionalChain | undefined, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentList: ReadonlyArray<Expression>) {
const node = <CallChain>createSynthesizedNode(SyntaxKind.CallChain);
node.chain = chain;
node.typeArguments = asNodeArray(typeArguments);
node.arguments = createNodeArray(argumentList);
return node;
}
export function updateCallChain(node: CallChain, chain: OptionalChain | undefined, typeArguments: ReadonlyArray<TypeNode> | undefined, argumentList: ReadonlyArray<Expression>) {
return node.chain !== chain
|| node.typeArguments !== typeArguments
|| node.arguments !== argumentList
? updateNode(createCallChain(chain, typeArguments, argumentList), node)
: node;
}
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));
@@ -2505,10 +2560,6 @@ 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);
}
+80 -58
View File
@@ -170,6 +170,19 @@ namespace ts {
case SyntaxKind.ElementAccessExpression:
return visitNode(cbNode, (<ElementAccessExpression>node).expression) ||
visitNode(cbNode, (<ElementAccessExpression>node).argumentExpression);
case SyntaxKind.OptionalExpression:
return visitNode(cbNode, (<OptionalExpression>node).expression) ||
visitNode(cbNode, (<OptionalExpression>node).chain);
case SyntaxKind.PropertyAccessChain:
return visitNode(cbNode, (<PropertyAccessChain>node).chain) ||
visitNode(cbNode, (<PropertyAccessChain>node).name);
case SyntaxKind.ElementAccessChain:
return visitNode(cbNode, (<ElementAccessChain>node).chain) ||
visitNode(cbNode, (<ElementAccessChain>node).argumentExpression);
case SyntaxKind.CallChain:
return visitNode(cbNode, (<CallChain>node).chain) ||
visitNodes(cbNode, cbNodes, (<CallChain>node).typeArguments) ||
visitNodes(cbNode, cbNodes, (<CallChain>node).arguments);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return visitNode(cbNode, (<CallExpression>node).expression) ||
@@ -2532,11 +2545,6 @@ 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();
@@ -3869,7 +3877,7 @@ namespace ts {
// Now, we *may* be complete. However, we might have consumed the start of a
// CallExpression. As such, we need to consume the rest of it here to be complete.
return parseCallExpressionRest(expression);
return parseOptionalExpressionRest(parseCallExpressionRest(expression));
}
function parseMemberExpressionOrHigher(): MemberExpression {
@@ -4191,22 +4199,9 @@ namespace ts {
function parseMemberExpressionRest(expression: LeftHandSideExpression): MemberExpression {
while (true) {
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 dotToken = parseOptionalToken(SyntaxKind.DotToken);
if (dotToken) {
const propertyAccess = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, expression.pos);
propertyAccess.flags = flags;
propertyAccess.expression = expression;
propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true);
expression = finishNode(propertyAccess);
@@ -4216,18 +4211,14 @@ 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
// however, `?.[` is unambiguously *not* a ComputedPropertyName.
const isOptionalElementAccess = isOptionalExpression && !isOptionalPropertyAccess;
if (isOptionalElementAccess || (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken))) {
if (!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[]".
@@ -4247,7 +4238,6 @@ 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()
@@ -4263,44 +4253,77 @@ 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) {
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;
}
// 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;
}
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.LessThanToken) {
return expression;
else if (token() === SyntaxKind.OpenParenToken) {
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
callExpr.expression = expression;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
continue;
}
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
callExpr.flags = flags;
callExpr.expression = expression;
callExpr.typeArguments = typeArguments;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
return expression;
}
}
function parseOptionalExpressionRest(expression: LeftHandSideExpression) {
while (token() === SyntaxKind.QuestionDotToken) {
const fullStart = getNodePos();
nextToken();
let chain: OptionalChain;
while (true) {
if (parseOptional(SyntaxKind.OpenBracketToken)) {
const elementAccessChain = createNode(SyntaxKind.ElementAccessChain, fullStart) as ElementAccessChain;
elementAccessChain.chain = chain;
elementAccessChain.argumentExpression = parseExpression();
parseExpected(SyntaxKind.CloseBracketToken);
chain = finishNode(elementAccessChain);
continue;
}
else if (token() === SyntaxKind.LessThanToken || token() === SyntaxKind.OpenParenToken) {
const callChain = createNode(SyntaxKind.CallChain, fullStart) as CallChain;
callChain.chain = chain;
callChain.typeArguments = parseTypeArgumentsInExpression();
callChain.arguments = parseArgumentList();
chain = finishNode(callChain);
continue;
}
else if (!chain || parseOptional(SyntaxKind.DotToken)) {
const propertyAccessChain = createNode(SyntaxKind.PropertyAccessChain, fullStart) as PropertyAccessChain;
propertyAccessChain.chain = chain;
propertyAccessChain.name = parseRightSideOfDot(/*allowIdentifierNames*/ true);
chain = finishNode(propertyAccessChain);
continue;
}
const node = createNode(SyntaxKind.OptionalExpression, expression.pos) as OptionalExpression;
node.expression = expression;
node.chain = chain;
expression = finishNode(node);
break;
}
}
return expression;
}
function parseArgumentList() {
parseExpected(SyntaxKind.OpenParenToken);
const result = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression);
@@ -4338,7 +4361,6 @@ 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> !=
+32 -94
View File
@@ -105,12 +105,8 @@ 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);
case SyntaxKind.OptionalExpression:
return visitOptionalExpression(node as OptionalExpression);
default:
return visitEachChild(node, visitor, context);
}
@@ -726,101 +722,43 @@ 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);
}
function visitOptionalChain(node: OptionalChain | undefined, expression: Expression) {
if (!node) return expression;
switch (node.kind) {
case SyntaxKind.PropertyAccessChain:
const propertyAccessExpression = createPropertyAccess(
visitOptionalChain(node.chain, expression),
visitNode(node.name, visitor, isIdentifier));
return propertyAccessExpression;
case SyntaxKind.ElementAccessChain:
const elementAccessExpression = createElementAccess(
visitOptionalChain(node.chain, expression),
visitNode(node.argumentExpression, visitor, isExpression));
return elementAccessExpression;
case SyntaxKind.CallChain:
const callExpression = createCall(
visitOptionalChain(node.chain, expression),
/*typeArguments*/ undefined,
visitNodes(node.arguments, visitor, isExpression));
return callExpression;
}
}
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);
}
function visitOptionalExpression(node: OptionalExpression) {
const root = visitNode(node.expression, visitor, isExpression);
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);
// setSourceMapRange(temp, root);
const condition = createLogicalOr(
createStrictEquality(createAssignment(temp, root), createNull()),
createStrictEquality(temp, createVoidZero()));
// setSourceMapRange(condition, root);
const chain = visitOptionalChain(node.chain, temp);
const conditional = createConditional(condition, createVoidZero(), chain);
// 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;
+12
View File
@@ -513,6 +513,9 @@ namespace ts {
// TypeScript namespace or external module import.
return visitImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.CallChain:
return visitCallChain(<CallChain>node);
default:
Debug.failBadSyntaxKind(node);
return visitEachChild(node, visitor, context);
@@ -2452,6 +2455,15 @@ namespace ts {
return createPartiallyEmittedExpression(expression, node);
}
function visitCallChain(node: CallChain) {
return updateCallChain(
node,
visitNode(node.chain, visitor, isOptionalChain),
/*typeArguments*/ undefined,
visitNodes(node.arguments, visitor, isExpression)
);
}
function visitCallExpression(node: CallExpression) {
return updateCall(
node,
+56 -27
View File
@@ -251,6 +251,7 @@ namespace ts {
PropertyAccessExpression,
ElementAccessExpression,
CallExpression,
OptionalExpression,
NewExpression,
TaggedTemplateExpression,
TypeAssertionExpression,
@@ -275,6 +276,11 @@ namespace ts {
NonNullExpression,
MetaProperty,
// Optional chains
PropertyAccessChain,
ElementAccessChain,
CallChain,
// Misc
TemplateSpan,
SemicolonClassElement,
@@ -383,7 +389,6 @@ namespace ts {
CommaListExpression,
MergeDeclarationMarker,
EndOfDeclarationMarker,
OptionalExpression,
// Enum value count
Count,
@@ -427,22 +432,20 @@ namespace ts {
NestedNamespace = 1 << 2, // Namespace declaration
Synthesized = 1 << 3, // Node was synthesized during transformation
Namespace = 1 << 4, // Namespace declaration
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
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
// 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
@@ -453,8 +456,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 << 20,
JSDoc = 1 << 21, // If node was parsed inside jsdoc
PossiblyContainsDynamicImport = 1 << 19,
JSDoc = 1 << 20, // If node was parsed inside jsdoc
BlockScoped = Let | Const,
@@ -462,12 +465,10 @@ namespace ts {
ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | OptionalChain,
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
// Exclude these flags when parsing a Type
TypeExcludesFlags = YieldContext | AwaitContext,
Optional = OptionalExpression | OptionalChain,
}
export const enum ModifierFlags {
@@ -1567,6 +1568,36 @@ namespace ts {
expression: ImportExpression;
}
export interface OptionalExpression extends MemberExpression {
kind: SyntaxKind.OptionalExpression;
expression: LeftHandSideExpression;
chain: OptionalChain;
}
export type OptionalChain = PropertyAccessChain | ElementAccessChain | CallChain;
export interface PropertyAccessChain extends Node {
kind: SyntaxKind.PropertyAccessChain;
parent?: OptionalChain | OptionalExpression;
chain?: OptionalChain;
name: Identifier;
}
export interface ElementAccessChain extends Node {
kind: SyntaxKind.ElementAccessChain;
chain?: OptionalChain;
parent?: OptionalChain | OptionalExpression;
argumentExpression: Expression;
}
export interface CallChain extends Node {
kind: SyntaxKind.CallChain;
chain?: OptionalChain;
parent?: OptionalChain | OptionalExpression;
typeArguments?: NodeArray<TypeNode>;
arguments: NodeArray<Expression>;
}
export interface ExpressionWithTypeArguments extends TypeNode {
kind: SyntaxKind.ExpressionWithTypeArguments;
parent?: HeritageClause;
@@ -1587,7 +1618,7 @@ namespace ts {
template: TemplateLiteral;
}
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement;
export type CallLikeExpression = CallExpression | CallChain | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement;
export interface AsExpression extends Expression {
kind: SyntaxKind.AsExpression;
@@ -3221,18 +3252,16 @@ 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 | Optional,
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean,
/* @internal */
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive | Optional,
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
/* @internal */
Primitive = String | Number | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal,
StringLike = String | StringLiteral | Index,
+14
View File
@@ -4464,6 +4464,19 @@ namespace ts {
return node.kind === SyntaxKind.MetaProperty;
}
export function isOptionalExpression(node: Node): node is OptionalExpression {
return node.kind === SyntaxKind.OptionalExpression;
}
// Optional chains
export function isOptionalChain(node: Node): node is OptionalChain {
const kind = node.kind;
return kind === SyntaxKind.PropertyAccessChain
|| kind === SyntaxKind.ElementAccessChain
|| kind === SyntaxKind.CallChain;
}
// Misc
export function isTemplateSpan(node: Node): node is TemplateSpan {
@@ -5201,6 +5214,7 @@ namespace ts {
case SyntaxKind.SuperKeyword:
case SyntaxKind.NonNullExpression:
case SyntaxKind.MetaProperty:
case SyntaxKind.OptionalExpression:
case SyntaxKind.ImportKeyword: // technically this is only an Expression if it's in a CallExpression
return true;
default:
+46
View File
@@ -446,6 +446,11 @@ namespace ts {
visitNode((<ElementAccessExpression>node).expression, visitor, isExpression),
visitNode((<ElementAccessExpression>node).argumentExpression, visitor, isExpression));
case SyntaxKind.OptionalExpression:
return updateOptionalExpression(<OptionalExpression>node,
visitNode((<OptionalExpression>node).expression, visitor, isExpression),
visitNode((<OptionalExpression>node).chain, visitor, isOptionalChain));
case SyntaxKind.CallExpression:
return updateCall(<CallExpression>node,
visitNode((<CallExpression>node).expression, visitor, isExpression),
@@ -569,6 +574,24 @@ namespace ts {
return updateMetaProperty(<MetaProperty>node,
visitNode((<MetaProperty>node).name, visitor, isIdentifier));
// Optional chains
case SyntaxKind.PropertyAccessChain:
return updatePropertyAccessChain(<PropertyAccessChain>node,
visitNode((<PropertyAccessChain>node).chain, visitor, isOptionalChain),
visitNode((<PropertyAccessChain>node).name, visitor, isIdentifier));
case SyntaxKind.ElementAccessChain:
return updateElementAccessChain(<ElementAccessChain>node,
visitNode((<ElementAccessChain>node).chain, visitor, isOptionalChain),
visitNode((<ElementAccessChain>node).argumentExpression, visitor, isExpression));
case SyntaxKind.CallChain:
return updateCallChain(<CallChain>node,
visitNode((<CallChain>node).chain, visitor, isOptionalChain),
nodesVisitor((<CallChain>node).typeArguments, visitor, isTypeNode),
nodesVisitor((<CallChain>node).arguments, visitor, isExpression));
// Misc
case SyntaxKind.TemplateSpan:
@@ -1063,6 +1086,11 @@ namespace ts {
result = reduceNode((<ElementAccessExpression>node).argumentExpression, cbNode, result);
break;
case SyntaxKind.OptionalExpression:
result = reduceNode((<OptionalExpression>node).expression, cbNode, result);
result = reduceNode((<OptionalExpression>node).chain, cbNode, result);
break;
case SyntaxKind.CallExpression:
result = reduceNode((<CallExpression>node).expression, cbNode, result);
result = reduceNodes((<CallExpression>node).typeArguments, cbNodes, result);
@@ -1152,6 +1180,24 @@ namespace ts {
result = reduceNode((<AsExpression>node).type, cbNode, result);
break;
// Optional chains
case SyntaxKind.PropertyAccessChain:
result = reduceNode((<PropertyAccessChain>node).chain, cbNode, result);
result = reduceNode((<PropertyAccessChain>node).name, cbNode, result);
break;
case SyntaxKind.ElementAccessChain:
result = reduceNode((<ElementAccessChain>node).chain, cbNode, result);
result = reduceNode((<ElementAccessChain>node).argumentExpression, cbNode, result);
break;
case SyntaxKind.CallChain:
result = reduceNode((<CallChain>node).chain, cbNode, result);
result = reduceNodes((<CallChain>node).typeArguments, cbNode, result);
result = reduceNodes((<CallChain>node).arguments, cbNode, result);
break;
// Misc
case SyntaxKind.TemplateSpan:
result = reduceNode((<TemplateSpan>node).expression, cbNode, result);