mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-21 07:26:21 -05:00
Fixed an issue with contextual type for intersection properties (take 2) (#52095)
This commit is contained in:
committed by
GitHub
parent
6894ff7f38
commit
e6edc567a3
@@ -31672,33 +31672,106 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(symbol as MappedSymbol).links.type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0);
|
||||
}
|
||||
|
||||
function isExcludedMappedPropertyName(constraint: Type, propertyNameType: Type): boolean {
|
||||
if (constraint.flags & TypeFlags.Conditional) {
|
||||
const type = constraint as ConditionalType;
|
||||
return !!(getReducedType(getTrueTypeFromConditionalType(type)).flags & TypeFlags.Never) &&
|
||||
getActualTypeVariable(getFalseTypeFromConditionalType(type)) === getActualTypeVariable(type.checkType) &&
|
||||
isTypeAssignableTo(propertyNameType, type.extendsType);
|
||||
}
|
||||
if (constraint.flags & TypeFlags.Intersection) {
|
||||
return some((constraint as IntersectionType).types, t => isExcludedMappedPropertyName(t, propertyNameType));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) {
|
||||
return mapType(type, t => {
|
||||
if (isGenericMappedType(t) && getMappedTypeNameTypeKind(t) !== MappedTypeNameTypeKind.Remapping) {
|
||||
const constraint = getConstraintTypeFromMappedType(t);
|
||||
const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint;
|
||||
const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name));
|
||||
if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) {
|
||||
return substituteIndexedMappedType(t, propertyNameType);
|
||||
if (t.flags & TypeFlags.Intersection) {
|
||||
let types: Type[] | undefined;
|
||||
let indexInfoCandidates: Type[] | undefined;
|
||||
let ignoreIndexInfos = false;
|
||||
for (const constituentType of (t as IntersectionType).types) {
|
||||
if (!(constituentType.flags & TypeFlags.Object)) {
|
||||
continue;
|
||||
}
|
||||
if (isGenericMappedType(constituentType) && getMappedTypeNameTypeKind(constituentType) !== MappedTypeNameTypeKind.Remapping) {
|
||||
const substitutedType = getIndexedMappedTypeSubstitutedTypeOfContextualType(constituentType, name, nameType);
|
||||
types = appendContextualPropertyTypeConstituent(types, substitutedType);
|
||||
continue;
|
||||
}
|
||||
const propertyType = getTypeOfConcretePropertyOfContextualType(constituentType, name);
|
||||
if (!propertyType) {
|
||||
if (!ignoreIndexInfos) {
|
||||
indexInfoCandidates = append(indexInfoCandidates, constituentType);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ignoreIndexInfos = true;
|
||||
indexInfoCandidates = undefined;
|
||||
types = appendContextualPropertyTypeConstituent(types, propertyType);
|
||||
}
|
||||
}
|
||||
else if (t.flags & TypeFlags.StructuredType) {
|
||||
const prop = getPropertyOfType(t, 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 (restType) {
|
||||
return restType;
|
||||
if (indexInfoCandidates) {
|
||||
for (const candidate of indexInfoCandidates) {
|
||||
const indexInfoType = getTypeFromIndexInfosOfContextualType(candidate, name, nameType);
|
||||
types = appendContextualPropertyTypeConstituent(types, indexInfoType);
|
||||
}
|
||||
}
|
||||
return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type;
|
||||
if (!types) {
|
||||
return;
|
||||
}
|
||||
if (types.length === 1) {
|
||||
return types[0];
|
||||
}
|
||||
return getIntersectionType(types);
|
||||
}
|
||||
return undefined;
|
||||
if (!(t.flags & TypeFlags.Object)) {
|
||||
return;
|
||||
}
|
||||
return isGenericMappedType(t) && getMappedTypeNameTypeKind(t) !== MappedTypeNameTypeKind.Remapping
|
||||
? getIndexedMappedTypeSubstitutedTypeOfContextualType(t, name, nameType)
|
||||
: getTypeOfConcretePropertyOfContextualType(t, name) ?? getTypeFromIndexInfosOfContextualType(t, name, nameType);
|
||||
}, /*noReductions*/ true);
|
||||
}
|
||||
|
||||
function appendContextualPropertyTypeConstituent(types: Type[] | undefined, type: Type | undefined) {
|
||||
// any doesn't provide any contextual information but could spoil the overall result by nullifying contextual information provided by other intersection constituents
|
||||
// so it gets replaced with `unknown` as `T & unknown` is just `T` and all types computed based on the contextual information provided by other constituens are still assignable to any
|
||||
return type ? append(types, type.flags & TypeFlags.Any ? unknownType : type) : types;
|
||||
}
|
||||
|
||||
function getIndexedMappedTypeSubstitutedTypeOfContextualType(type: MappedType, name: __String, nameType: Type | undefined) {
|
||||
const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name));
|
||||
const constraint = getConstraintTypeFromMappedType(type);
|
||||
// special case for conditional types pretending to be negated types
|
||||
if (type.nameType && isExcludedMappedPropertyName(type.nameType, propertyNameType) || isExcludedMappedPropertyName(constraint, propertyNameType)) {
|
||||
return;
|
||||
}
|
||||
const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint;
|
||||
if (!isTypeAssignableTo(propertyNameType, constraintOfConstraint)) {
|
||||
return;
|
||||
}
|
||||
return substituteIndexedMappedType(type, propertyNameType);
|
||||
}
|
||||
|
||||
function getTypeOfConcretePropertyOfContextualType(type: Type, name: __String) {
|
||||
const prop = getPropertyOfType(type, name);
|
||||
if (!prop || isCircularMappedProperty(prop)) {
|
||||
return;
|
||||
}
|
||||
return removeMissingType(getTypeOfSymbol(prop), !!(prop.flags & SymbolFlags.Optional));
|
||||
}
|
||||
|
||||
function getTypeFromIndexInfosOfContextualType(type: Type, name: __String, nameType: Type | undefined) {
|
||||
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(type), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type;
|
||||
}
|
||||
|
||||
// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of
|
||||
// the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one
|
||||
// exists. Otherwise, it is the type of the string index signature in T, if one exists.
|
||||
|
||||
Reference in New Issue
Block a user