@@ -19882,6 +19882,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
19882
19882
}
19883
19883
}
19884
19884
if (sourceFlags & TypeFlags.Conditional) {
19885
+ if ((source as ConditionalType).root === (target as ConditionalType).root) {
19886
+ // Two instantiations of the same conditional type, just check instantiated outer type parameter equality
19887
+ const params = (source as ConditionalType).root.outerTypeParameters || [];
19888
+ const sourceTypeArguments = map(params, t => (source as ConditionalType).mapper ? getMappedType(t, (source as ConditionalType).mapper!) : t);
19889
+ const targetTypeArguments = map(params, t => (target as ConditionalType).mapper ? getMappedType(t, (target as ConditionalType).mapper!) : t);
19890
+ return typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, map(params, () => VarianceFlags.Unmeasurable), /*reportErrors*/ false, IntersectionState.None);
19891
+ }
19885
19892
if ((source as ConditionalType).root.isDistributive === (target as ConditionalType).root.isDistributive) {
19886
19893
if (result = isRelatedTo((source as ConditionalType).checkType, (target as ConditionalType).checkType, RecursionFlags.Both, /*reportErrors*/ false)) {
19887
19894
if (result &= isRelatedTo((source as ConditionalType).extendsType, (target as ConditionalType).extendsType, RecursionFlags.Both, /*reportErrors*/ false)) {
@@ -26071,6 +26078,24 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
26071
26078
node.kind === SyntaxKind.PropertyDeclaration)!;
26072
26079
}
26073
26080
26081
+ function getControlFlowContainerForIdentifier(node: Identifier, declaration: Declaration, symbol: Symbol, typeFromSymbol: Type) {
26082
+ // The declaration container is the innermost function that encloses the declaration of the variable
26083
+ // or parameter. The flow container is the innermost function starting with which we analyze the control
26084
+ // flow graph to determine the control flow based type.
26085
+ const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
26086
+ const declarationContainer = getControlFlowContainer(declaration);
26087
+ let flowContainer = getControlFlowContainer(node);
26088
+ // When the control flow originates in a function expression or arrow function and we are referencing
26089
+ // a const variable or parameter from an outer function, we extend the origin of the control flow
26090
+ // analysis to include the immediately enclosing function.
26091
+ while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
26092
+ flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) &&
26093
+ (isConstVariable(symbol) && typeFromSymbol !== autoArrayType || isParameter && !isSymbolAssigned(symbol))) {
26094
+ flowContainer = getControlFlowContainer(flowContainer);
26095
+ }
26096
+ return flowContainer;
26097
+ }
26098
+
26074
26099
// Check if a parameter or catch variable is assigned anywhere
26075
26100
function isSymbolAssigned(symbol: Symbol) {
26076
26101
if (!symbol.valueDeclaration) {
@@ -26432,24 +26457,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
26432
26457
}
26433
26458
26434
26459
type = getNarrowableTypeForReference(type, node, checkMode);
26435
-
26436
- // The declaration container is the innermost function that encloses the declaration of the variable
26437
- // or parameter. The flow container is the innermost function starting with which we analyze the control
26438
- // flow graph to determine the control flow based type.
26439
26460
const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
26440
26461
const declarationContainer = getControlFlowContainer(declaration);
26441
- let flowContainer = getControlFlowContainer(node);
26442
- const isOuterVariable = flowContainer !== declarationContainer;
26443
- const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent);
26462
+ const flowContainer = getControlFlowContainerForIdentifier(node, declaration, localOrExportSymbol, type);
26444
26463
const isModuleExports = symbol.flags & SymbolFlags.ModuleExports;
26445
- // When the control flow originates in a function expression or arrow function and we are referencing
26446
- // a const variable or parameter from an outer function, we extend the origin of the control flow
26447
- // analysis to include the immediately enclosing function.
26448
- while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
26449
- flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) &&
26450
- (isConstVariable(localOrExportSymbol) && type !== autoArrayType || isParameter && !isSymbolAssigned(localOrExportSymbol))) {
26451
- flowContainer = getControlFlowContainer(flowContainer);
26452
- }
26464
+ const isOuterVariable = getControlFlowContainer(node) !== declarationContainer;
26465
+ const isSpreadDestructuringAssignmentTarget = node.parent && node.parent.parent && isSpreadAssignment(node.parent) && isDestructuringAssignmentTarget(node.parent.parent);
26453
26466
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
26454
26467
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
26455
26468
// declaration container are the same).
@@ -32762,23 +32775,29 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
32762
32775
}
32763
32776
32764
32777
function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) {
32765
- let exprType = checkExpression(expression, checkMode);
32778
+ const exprType = checkExpression(expression, checkMode);
32766
32779
if (isConstTypeReference(type)) {
32767
32780
if (!isValidConstAssertionArgument(expression)) {
32768
32781
error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals);
32769
32782
}
32770
32783
return getRegularTypeOfLiteralType(exprType);
32771
32784
}
32772
32785
checkSourceElement(type);
32773
- exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType));
32774
32786
const targetType = getTypeFromTypeNode(type);
32775
32787
if (!isErrorType(targetType)) {
32776
32788
addLazyDiagnostic(() => {
32777
- const widenedType = getWidenedType(exprType);
32789
+ const regularType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType));
32790
+ const widenedType = getWidenedType(regularType);
32778
32791
if (!isTypeComparableTo(targetType, widenedType)) {
32779
- checkTypeComparableTo(exprType , targetType, errNode,
32792
+ checkTypeComparableTo(regularType , targetType, errNode,
32780
32793
Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first);
32781
32794
}
32795
+ // Assertions, as a side effect, disable widening - some casts may rely on this, so
32796
+ // if the expression type was widenable, we shouldn't assume the cast is extraneous.
32797
+ // (Generally speaking, such casts are better served with `as const` casts nowadays though!)
32798
+ else if (widenedType === exprType && isTypeIdenticalTo(targetType, widenedType)) {
32799
+ errorOrSuggestion(!!compilerOptions.noUnnecessaryCasts, errNode, Diagnostics.Type_cast_has_no_effect_on_the_type_of_this_expression);
32800
+ }
32782
32801
});
32783
32802
}
32784
32803
return targetType;
@@ -32791,8 +32810,37 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
32791
32810
}
32792
32811
32793
32812
function checkNonNullAssertion(node: NonNullExpression) {
32794
- return node.flags & NodeFlags.OptionalChain ? checkNonNullChain(node as NonNullChain) :
32795
- getNonNullableType(checkExpression(node.expression));
32813
+ const exprType = checkExpression(node.expression);
32814
+ const resultType = node.flags & NodeFlags.OptionalChain ? checkNonNullChain(node as NonNullChain) : getNonNullableType(exprType);
32815
+ if (!isErrorType(resultType)) {
32816
+ addLazyDiagnostic(() => {
32817
+ if (isTypeIdenticalTo(exprType, resultType)) {
32818
+ // A non-null assertion on an identifier may also suppress a used-before definition, so may still be useful even if the type is unchanged by it
32819
+ // Identifiers on the left-hand-side of an assignment expression, ofc, can't do this, since they're for the assigned type itself.
32820
+ if (isIdentifier(node.expression) && !containsUndefinedType(exprType) && !(isAssignmentExpression(node.parent) && node.parent.left === node)) {
32821
+ const symbol = getSymbolAtLocation(node.expression);
32822
+ const declaration = symbol?.valueDeclaration;
32823
+ if (declaration) {
32824
+ const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
32825
+ if (!isParameter) {
32826
+ const flowContainer = getControlFlowContainerForIdentifier(node.expression, declaration, symbol, exprType);
32827
+ // We need a fake reference that isn't parented to the nonnull expression, since the non-null will affect control flow's result
32828
+ // by suppressing the initial return type at the end of the flow
32829
+ const fakeReference = factory.cloneNode(node.expression);
32830
+ setParent(fakeReference, node.parent);
32831
+ fakeReference.flowNode = node.expression.flowNode;
32832
+ const flowType = getFlowTypeOfReference(fakeReference, exprType, undefinedType, flowContainer);
32833
+ if (containsUndefinedType(flowType)) {
32834
+ return; // Cast is required to suppress a use before assignment control flow error
32835
+ }
32836
+ }
32837
+ }
32838
+ }
32839
+ errorOrSuggestion(!!compilerOptions.noUnnecessaryCasts, node, Diagnostics.Type_cast_has_no_effect_on_the_type_of_this_expression);
32840
+ }
32841
+ });
32842
+ }
32843
+ return resultType;
32796
32844
}
32797
32845
32798
32846
function checkExpressionWithTypeArguments(node: ExpressionWithTypeArguments | TypeQueryNode) {
0 commit comments