@@ -9943,13 +9943,13 @@ namespace ts {
9943
9943
return type.flags & TypeFlags.Union ? getIntersectionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
9944
9944
type.flags & TypeFlags.Intersection ? getUnionType(map((<IntersectionType>type).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
9945
9945
maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type, stringsOnly) :
9946
- getObjectFlags(type) & ObjectFlags.Mapped ? filterType(getConstraintTypeFromMappedType(<MappedType>type), t => !(noIndexSignatures && t.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number ))) :
9946
+ getObjectFlags(type) & ObjectFlags.Mapped ? filterType(getConstraintTypeFromMappedType(<MappedType>type), t => !(noIndexSignatures && t.flags & (TypeFlags.Any | TypeFlags.String))) :
9947
9947
type === wildcardType ? wildcardType :
9948
9948
type.flags & TypeFlags.Unknown ? neverType :
9949
9949
type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType :
9950
9950
stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral) :
9951
9951
!noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol)]) :
9952
- !noIndexSignatures && getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) :
9952
+ getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) :
9953
9953
getLiteralTypeFromProperties(type, TypeFlags.StringOrNumberLiteralOrUnique);
9954
9954
}
9955
9955
@@ -10067,10 +10067,10 @@ namespace ts {
10067
10067
if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) {
10068
10068
return objectType;
10069
10069
}
10070
- const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
10071
- getIndexInfoOfType(objectType, IndexKind.String) ;
10070
+ const stringIndexInfo = getIndexInfoOfType(objectType, IndexKind.String);
10071
+ const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) || stringIndexInfo ;
10072
10072
if (indexInfo) {
10073
- if (accessFlags & AccessFlags.NoIndexSignatures) {
10073
+ if (accessFlags & AccessFlags.NoIndexSignatures && indexInfo === stringIndexInfo ) {
10074
10074
if (accessExpression) {
10075
10075
error(accessExpression, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(originalObjectType));
10076
10076
}
@@ -14228,6 +14228,32 @@ namespace ts {
14228
14228
return strictNullChecks ? getGlobalNonNullableTypeInstantiation(type) : type;
14229
14229
}
14230
14230
14231
+
14232
+ /**
14233
+ * Is source potentially coercible to target type under `==`.
14234
+ * Assumes that `source` is a constituent of a union, hence
14235
+ * the boolean literal flag on the LHS, but not on the RHS.
14236
+ *
14237
+ * This does not fully replicate the semantics of `==`. The
14238
+ * intention is to catch cases that are clearly not right.
14239
+ *
14240
+ * Comparing (string | number) to number should not remove the
14241
+ * string element.
14242
+ *
14243
+ * Comparing (string | number) to 1 will remove the string
14244
+ * element, though this is not sound. This is a pragmatic
14245
+ * choice.
14246
+ *
14247
+ * @see narrowTypeByEquality
14248
+ *
14249
+ * @param source
14250
+ * @param target
14251
+ */
14252
+ function isCoercibleUnderDoubleEquals(source: Type, target: Type): boolean {
14253
+ return ((source.flags & (TypeFlags.Number | TypeFlags.String | TypeFlags.BooleanLiteral)) !== 0)
14254
+ && ((target.flags & (TypeFlags.Number | TypeFlags.String | TypeFlags.Boolean)) !== 0);
14255
+ }
14256
+
14231
14257
/**
14232
14258
* Return true if type was inferred from an object literal, written as an object type literal, or is the shape of a module
14233
14259
* with no call or construct signatures.
@@ -16598,7 +16624,10 @@ namespace ts {
16598
16624
return type;
16599
16625
}
16600
16626
if (assumeTrue) {
16601
- const narrowedType = filterType(type, t => areTypesComparable(t, valueType));
16627
+ const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
16628
+ (t => areTypesComparable(t, valueType) || isCoercibleUnderDoubleEquals(t, valueType)) :
16629
+ t => areTypesComparable(t, valueType);
16630
+ const narrowedType = filterType(type, filterFn);
16602
16631
return narrowedType.flags & TypeFlags.Never ? type : replacePrimitivesWithLiterals(narrowedType, valueType);
16603
16632
}
16604
16633
if (isUnitType(valueType)) {
@@ -26461,14 +26490,28 @@ namespace ts {
26461
26490
}
26462
26491
// For a binding pattern, validate the initializer and exit
26463
26492
if (isBindingPattern(node.name)) {
26464
- // Don't validate for-in initializer as it is already an error
26465
- if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
26466
- const initializerType = checkExpressionCached(node.initializer);
26467
- if (strictNullChecks && node.name.elements.length === 0) {
26468
- checkNonNullNonVoidType(initializerType, node);
26493
+ const needCheckInitializer = node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement;
26494
+ const needCheckWidenedType = node.name.elements.length === 0;
26495
+ if (needCheckInitializer || needCheckWidenedType) {
26496
+ // Don't validate for-in initializer as it is already an error
26497
+ const widenedType = getWidenedTypeForVariableLikeDeclaration(node);
26498
+ if (needCheckInitializer) {
26499
+ const initializerType = checkExpressionCached(node.initializer!);
26500
+ if (strictNullChecks && needCheckWidenedType) {
26501
+ checkNonNullNonVoidType(initializerType, node);
26502
+ }
26503
+ else {
26504
+ checkTypeAssignableToAndOptionallyElaborate(initializerType, getWidenedTypeForVariableLikeDeclaration(node), node, node.initializer);
26505
+ }
26469
26506
}
26470
- else {
26471
- checkTypeAssignableToAndOptionallyElaborate(initializerType, getWidenedTypeForVariableLikeDeclaration(node), node, node.initializer);
26507
+ // check the binding pattern with empty elements
26508
+ if (needCheckWidenedType) {
26509
+ if (isArrayBindingPattern(node.name)) {
26510
+ checkIteratedTypeOrElementType(widenedType, node, /* allowStringInput */ false, /* allowAsyncIterables */ false);
26511
+ }
26512
+ else if (strictNullChecks) {
26513
+ checkNonNullNonVoidType(widenedType, node);
26514
+ }
26472
26515
}
26473
26516
}
26474
26517
return;
0 commit comments