mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Optional chaining prototype
This commit is contained in:
+14
-1
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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> !=
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user