@@ -21998,9 +21998,35 @@ namespace ts {
21998
21998
(containsTruthyCheck(source, (target as BinaryExpression).left) || containsTruthyCheck(source, (target as BinaryExpression).right)));
21999
21999
}
22000
22000
22001
- function getAccessedPropertyName(access: AccessExpression): __String | undefined {
22001
+ function getPropertyAccess(expr: Expression) {
22002
+ if (isAccessExpression(expr)) {
22003
+ return expr;
22004
+ }
22005
+ if (isIdentifier(expr)) {
22006
+ const symbol = getResolvedSymbol(expr);
22007
+ if (isConstVariable(symbol)) {
22008
+ const declaration = symbol.valueDeclaration!;
22009
+ // Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
22010
+ if (isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer)) {
22011
+ return declaration.initializer;
22012
+ }
22013
+ // Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
22014
+ if (isBindingElement(declaration) && !declaration.initializer) {
22015
+ const parent = declaration.parent.parent;
22016
+ if (isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer))) {
22017
+ return declaration;
22018
+ }
22019
+ }
22020
+ }
22021
+ }
22022
+ return undefined;
22023
+ }
22024
+
22025
+ function getAccessedPropertyName(access: AccessExpression | BindingElement): __String | undefined {
22026
+ let propertyName;
22002
22027
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
22003
- isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
22028
+ access.kind === SyntaxKind.ElementAccessExpression && isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
22029
+ access.kind === SyntaxKind.BindingElement && (propertyName = getDestructuringPropertyName(access)) ? escapeLeadingUnderscores(propertyName) :
22004
22030
undefined;
22005
22031
}
22006
22032
@@ -22948,14 +22974,15 @@ namespace ts {
22948
22974
}
22949
22975
}
22950
22976
22951
- function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer ?: Node, couldBeUninitialized ?: boolean ) {
22977
+ function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, isConstant ?: boolean, flowContainer ?: Node ) {
22952
22978
let key: string | undefined;
22953
22979
let isKeySet = false;
22954
22980
let flowDepth = 0;
22981
+ let inlineLevel = 0;
22955
22982
if (flowAnalysisDisabled) {
22956
22983
return errorType;
22957
22984
}
22958
- if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable) ) {
22985
+ if (!reference.flowNode) {
22959
22986
return declaredType;
22960
22987
}
22961
22988
flowInvocationCount++;
@@ -23244,8 +23271,9 @@ namespace ts {
23244
23271
t => !(t.flags & TypeFlags.Never || t.flags & TypeFlags.StringLiteral && (t as StringLiteralType).value === "undefined"));
23245
23272
}
23246
23273
}
23247
- if (isMatchingReferenceDiscriminant(expr, type)) {
23248
- type = narrowTypeBySwitchOnDiscriminantProperty(type, expr as AccessExpression, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
23274
+ const access = getDiscriminantPropertyAccess(expr, type);
23275
+ if (access) {
23276
+ type = narrowTypeBySwitchOnDiscriminantProperty(type, access, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
23249
23277
}
23250
23278
}
23251
23279
return createFlowType(type, isIncomplete(flowType));
@@ -23402,19 +23430,16 @@ namespace ts {
23402
23430
return result;
23403
23431
}
23404
23432
23405
- function isMatchingReferenceDiscriminant(expr: Expression, computedType: Type) {
23433
+ function getDiscriminantPropertyAccess(expr: Expression, computedType: Type) {
23434
+ let access, name;
23406
23435
const type = declaredType.flags & TypeFlags.Union ? declaredType : computedType;
23407
- if (!(type.flags & TypeFlags.Union) || !isAccessExpression(expr)) {
23408
- return false;
23409
- }
23410
- const name = getAccessedPropertyName(expr);
23411
- if (name === undefined) {
23412
- return false;
23413
- }
23414
- return isMatchingReference(reference, expr.expression) && isDiscriminantProperty(type, name);
23436
+ return type.flags & TypeFlags.Union && (access = getPropertyAccess(expr)) && (name = getAccessedPropertyName(access)) &&
23437
+ isMatchingReference(reference, isAccessExpression(access) ? access.expression : access.parent.parent.initializer!) &&
23438
+ isDiscriminantProperty(type, name) ?
23439
+ access : undefined;
23415
23440
}
23416
23441
23417
- function narrowTypeByDiscriminant(type: Type, access: AccessExpression, narrowType: (t: Type) => Type): Type {
23442
+ function narrowTypeByDiscriminant(type: Type, access: AccessExpression | BindingElement , narrowType: (t: Type) => Type): Type {
23418
23443
const propName = getAccessedPropertyName(access);
23419
23444
if (propName === undefined) {
23420
23445
return type;
@@ -23432,7 +23457,7 @@ namespace ts {
23432
23457
});
23433
23458
}
23434
23459
23435
- function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression, operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
23460
+ function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression | BindingElement , operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
23436
23461
if ((operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) && type.flags & TypeFlags.Union) {
23437
23462
const keyPropertyName = getKeyPropertyName(type as UnionType);
23438
23463
if (keyPropertyName && keyPropertyName === getAccessedPropertyName(access)) {
@@ -23447,7 +23472,7 @@ namespace ts {
23447
23472
return narrowTypeByDiscriminant(type, access, t => narrowTypeByEquality(t, operator, value, assumeTrue));
23448
23473
}
23449
23474
23450
- function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
23475
+ function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression | BindingElement , switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
23451
23476
if (clauseStart < clauseEnd && type.flags & TypeFlags.Union && getKeyPropertyName(type as UnionType) === getAccessedPropertyName(access)) {
23452
23477
const clauseTypes = getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd);
23453
23478
const candidate = getUnionType(map(clauseTypes, t => getConstituentTypeForKeyType(type as UnionType, t) || unknownType));
@@ -23465,8 +23490,9 @@ namespace ts {
23465
23490
if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) {
23466
23491
type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
23467
23492
}
23468
- if (isMatchingReferenceDiscriminant(expr, type)) {
23469
- return narrowTypeByDiscriminant(type, expr as AccessExpression, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
23493
+ const access = getDiscriminantPropertyAccess(expr, type);
23494
+ if (access) {
23495
+ return narrowTypeByDiscriminant(type, access, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
23470
23496
}
23471
23497
return type;
23472
23498
}
@@ -23524,11 +23550,13 @@ namespace ts {
23524
23550
type = narrowTypeByOptionalChainContainment(type, operator, left, assumeTrue);
23525
23551
}
23526
23552
}
23527
- if (isMatchingReferenceDiscriminant(left, type)) {
23528
- return narrowTypeByDiscriminantProperty(type, left as AccessExpression, operator, right, assumeTrue);
23553
+ const leftAccess = getDiscriminantPropertyAccess(left, type);
23554
+ if (leftAccess) {
23555
+ return narrowTypeByDiscriminantProperty(type, leftAccess, operator, right, assumeTrue);
23529
23556
}
23530
- if (isMatchingReferenceDiscriminant(right, type)) {
23531
- return narrowTypeByDiscriminantProperty(type, right as AccessExpression, operator, left, assumeTrue);
23557
+ const rightAccess = getDiscriminantPropertyAccess(right, type);
23558
+ if (rightAccess) {
23559
+ return narrowTypeByDiscriminantProperty(type, rightAccess, operator, left, assumeTrue);
23532
23560
}
23533
23561
if (isMatchingConstructorReference(left)) {
23534
23562
return narrowTypeByConstructor(type, operator, right, assumeTrue);
@@ -23553,6 +23581,17 @@ namespace ts {
23553
23581
break;
23554
23582
case SyntaxKind.CommaToken:
23555
23583
return narrowType(type, expr.right, assumeTrue);
23584
+ // Ordinarily we won't see && and || expressions in control flow analysis because the Binder breaks those
23585
+ // expressions down to individual conditional control flows. However, we may encounter them when analyzing
23586
+ // aliased conditional expressions.
23587
+ case SyntaxKind.AmpersandAmpersandToken:
23588
+ return assumeTrue ?
23589
+ narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ true) :
23590
+ getUnionType([narrowType(type, expr.left, /*assumeTrue*/ false), narrowType(type, expr.right, /*assumeTrue*/ false)]);
23591
+ case SyntaxKind.BarBarToken:
23592
+ return assumeTrue ?
23593
+ getUnionType([narrowType(type, expr.left, /*assumeTrue*/ true), narrowType(type, expr.right, /*assumeTrue*/ true)]) :
23594
+ narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ false);
23556
23595
}
23557
23596
return type;
23558
23597
}
@@ -23959,8 +23998,9 @@ namespace ts {
23959
23998
!(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) {
23960
23999
type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
23961
24000
}
23962
- if (isMatchingReferenceDiscriminant(predicateArgument, type)) {
23963
- return narrowTypeByDiscriminant(type, predicateArgument as AccessExpression, t => getNarrowedType(t, predicate.type!, assumeTrue, isTypeSubtypeOf));
24001
+ const access = getDiscriminantPropertyAccess(predicateArgument, type);
24002
+ if (access) {
24003
+ return narrowTypeByDiscriminant(type, access, t => getNarrowedType(t, predicate.type!, assumeTrue, isTypeSubtypeOf));
23964
24004
}
23965
24005
}
23966
24006
}
@@ -23977,6 +24017,21 @@ namespace ts {
23977
24017
}
23978
24018
switch (expr.kind) {
23979
24019
case SyntaxKind.Identifier:
24020
+ // When narrowing a reference to a const variable, non-assigned parameter, or readonly property, we inline
24021
+ // up to two levels of aliased conditional expressions that are themselves declared as const variables.
24022
+ if (isConstant && !isMatchingReference(reference, expr) && inlineLevel < 2) {
24023
+ const symbol = getResolvedSymbol(expr as Identifier);
24024
+ if (isConstVariable(symbol)) {
24025
+ const declaration = symbol.valueDeclaration;
24026
+ if (declaration && isVariableDeclaration(declaration) && !declaration.type && declaration.initializer) {
24027
+ inlineLevel++;
24028
+ const result = narrowType(type, declaration.initializer, assumeTrue);
24029
+ inlineLevel--;
24030
+ return result;
24031
+ }
24032
+ }
24033
+ }
24034
+ // falls through
23980
24035
case SyntaxKind.ThisKeyword:
23981
24036
case SyntaxKind.SuperKeyword:
23982
24037
case SyntaxKind.PropertyAccessExpression:
@@ -24002,8 +24057,9 @@ namespace ts {
24002
24057
if (isMatchingReference(reference, expr)) {
24003
24058
return getTypeWithFacts(type, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull);
24004
24059
}
24005
- if (isMatchingReferenceDiscriminant(expr, type)) {
24006
- return narrowTypeByDiscriminant(type, expr as AccessExpression, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull));
24060
+ const access = getDiscriminantPropertyAccess(expr, type);
24061
+ if (access) {
24062
+ return narrowTypeByDiscriminant(type, access, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull));
24007
24063
}
24008
24064
return type;
24009
24065
}
@@ -24081,7 +24137,7 @@ namespace ts {
24081
24137
}
24082
24138
24083
24139
function isConstVariable(symbol: Symbol) {
24084
- return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 && getTypeOfSymbol(symbol) !== autoArrayType ;
24140
+ return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0;
24085
24141
}
24086
24142
24087
24143
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */
@@ -24313,12 +24369,12 @@ namespace ts {
24313
24369
const isOuterVariable = flowContainer !== declarationContainer;
24314
24370
const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent);
24315
24371
const isModuleExports = symbol.flags & SymbolFlags.ModuleExports;
24372
+ const isConstant = isConstVariable(localOrExportSymbol) && getTypeOfSymbol(localOrExportSymbol) !== autoArrayType || isParameter && !isParameterAssigned(localOrExportSymbol);
24316
24373
// When the control flow originates in a function expression or arrow function and we are referencing
24317
24374
// a const variable or parameter from an outer function, we extend the origin of the control flow
24318
24375
// analysis to include the immediately enclosing function.
24319
- while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
24320
- flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethod(flowContainer)) &&
24321
- (isConstVariable(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) {
24376
+ while (isConstant && flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
24377
+ flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethod(flowContainer))) {
24322
24378
flowContainer = getControlFlowContainer(flowContainer);
24323
24379
}
24324
24380
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
@@ -24333,7 +24389,7 @@ namespace ts {
24333
24389
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, declaration as VariableLikeDeclaration) : type) :
24334
24390
type === autoType || type === autoArrayType ? undefinedType :
24335
24391
getOptionalType(type);
24336
- const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer, !assumeInitialized );
24392
+ const flowType = getFlowTypeOfReference(node, type, initialType, isConstant, flowContainer );
24337
24393
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
24338
24394
// from declaration to use, and when the variable's declared type doesn't include undefined but the
24339
24395
// control flow based type does include undefined.
@@ -27547,7 +27603,7 @@ namespace ts {
27547
27603
getControlFlowContainer(node) === getControlFlowContainer(prop.valueDeclaration)) {
27548
27604
assumeUninitialized = true;
27549
27605
}
27550
- const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType);
27606
+ const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType, prop && isReadonlySymbol(prop) );
27551
27607
if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
27552
27608
error(errorNode, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217
27553
27609
// Return the declared type to reduce follow-on errors
@@ -37991,7 +38047,7 @@ namespace ts {
37991
38047
error(node.name, Diagnostics.Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context);
37992
38048
}
37993
38049
37994
- const isAmbientExternalModule = isAmbientModule(node);
38050
+ const isAmbientExternalModule: boolean = isAmbientModule(node);
37995
38051
const contextErrorMessage = isAmbientExternalModule
37996
38052
? Diagnostics.An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file
37997
38053
: Diagnostics.A_namespace_declaration_is_only_allowed_in_a_namespace_or_module;
0 commit comments