From dae40d5f7bdfb4d2c32cd7441ee04792d73b6e2e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Aug 2024 11:01:30 -0700 Subject: [PATCH] Map any to unknown in contextual intersection types for elements --- src/compiler/checker.ts | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6233873ab6d..4ec45c9c553 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31578,30 +31578,40 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) { - return mapType(type, t => { - if (isGenericMappedType(t) && !t.declaration.nameType) { - const constraint = getConstraintTypeFromMappedType(t); + return mapType(type, getTypeOfProperty, /*noReductions*/ true); + function getTypeOfProperty(type: Type): Type | undefined { + if (type.flags & TypeFlags.Intersection) { + // Intersections that include `any` resolve to `any`. This loses potentially useful type + // information contributed by other types in the intersection, so we map `any` to `unknown` + // when we intersect the contextual property types. + return getIntersectionType(map((type as IntersectionType).types, t => { + const propType = getTypeOfProperty(t); + return !propType || propType.flags & TypeFlags.Any ? unknownType : propType; + })); + } + else if (isGenericMappedType(type) && !type.declaration.nameType) { + const constraint = getConstraintTypeFromMappedType(type); const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint; const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name)); if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) { - return substituteIndexedMappedType(t, propertyNameType); + return substituteIndexedMappedType(type, propertyNameType); } } - else if (t.flags & TypeFlags.StructuredType) { - const prop = getPropertyOfType(t, name); + else if (type.flags & TypeFlags.Object) { + const prop = getPropertyOfType(type, name); if (prop) { return isCircularMappedProperty(prop) ? undefined : removeMissingType(getTypeOfSymbol(prop), !!(prop.flags & SymbolFlags.Optional)); } - if (isTupleType(t) && isNumericLiteralName(name) && +name >= 0) { - const restType = getElementTypeOfSliceOfTupleType(t, t.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true); + if (isTupleType(type) && isNumericLiteralName(name) && +name >= 0) { + const restType = getElementTypeOfSliceOfTupleType(type, type.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true); if (restType) { return restType; } } - return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type; + return findApplicableIndexInfo(getIndexInfosOfStructuredType(type), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type; } return undefined; - }, /*noReductions*/ true); + } } // In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of