mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 11:24:49 -05:00
Defer union or intersection property type normalization (#31486)
* Defer union or intersection property type normalization * Accept moved span
This commit is contained in:
@@ -5921,7 +5921,20 @@ namespace ts {
|
||||
return anyType;
|
||||
}
|
||||
|
||||
function getTypeOfSymbolWithDeferredType(symbol: Symbol) {
|
||||
const links = getSymbolLinks(symbol);
|
||||
if (!links.type) {
|
||||
Debug.assertDefined(links.deferralParent);
|
||||
Debug.assertDefined(links.deferralConstituents);
|
||||
links.type = links.deferralParent!.flags & TypeFlags.Union ? getUnionType(links.deferralConstituents!) : getIntersectionType(links.deferralConstituents!);
|
||||
}
|
||||
return links.type;
|
||||
}
|
||||
|
||||
function getTypeOfSymbol(symbol: Symbol): Type {
|
||||
if (getCheckFlags(symbol) & CheckFlags.DeferredType) {
|
||||
return getTypeOfSymbolWithDeferredType(symbol);
|
||||
}
|
||||
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
|
||||
return getTypeOfInstantiatedSymbol(symbol);
|
||||
}
|
||||
@@ -8061,7 +8074,15 @@ namespace ts {
|
||||
|
||||
result.declarations = declarations!;
|
||||
result.nameType = nameType;
|
||||
result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes);
|
||||
if (propTypes.length > 2) {
|
||||
// When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed
|
||||
result.checkFlags |= CheckFlags.DeferredType;
|
||||
result.deferralParent = containingType;
|
||||
result.deferralConstituents = propTypes;
|
||||
}
|
||||
else {
|
||||
result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -12313,8 +12334,8 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isIgnoredJsxProperty(source: Type, sourceProp: Symbol, targetMemberType: Type | undefined) {
|
||||
return getObjectFlags(source) & ObjectFlags.JsxAttributes && !(isUnhyphenatedJsxName(sourceProp.escapedName) || targetMemberType);
|
||||
function isIgnoredJsxProperty(source: Type, sourceProp: Symbol) {
|
||||
return getObjectFlags(source) & ObjectFlags.JsxAttributes && !isUnhyphenatedJsxName(sourceProp.escapedName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13495,6 +13516,49 @@ namespace ts {
|
||||
return result || properties;
|
||||
}
|
||||
|
||||
function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary {
|
||||
const targetIsOptional = strictNullChecks && !!(getCheckFlags(targetProp) & CheckFlags.Partial);
|
||||
const source = getTypeOfSourceProperty(sourceProp);
|
||||
if (getCheckFlags(targetProp) & CheckFlags.DeferredType && !getSymbolLinks(targetProp).type) {
|
||||
// Rather than resolving (and normalizing) the type, relate constituent-by-constituent without performing normalization or seconadary passes
|
||||
const links = getSymbolLinks(targetProp);
|
||||
Debug.assertDefined(links.deferralParent);
|
||||
Debug.assertDefined(links.deferralConstituents);
|
||||
const unionParent = !!(links.deferralParent!.flags & TypeFlags.Union);
|
||||
let result = unionParent ? Ternary.False : Ternary.True;
|
||||
const targetTypes = links.deferralConstituents!;
|
||||
for (const targetType of targetTypes) {
|
||||
const related = isRelatedTo(source, targetType, /*reportErrors*/ false, /*headMessage*/ undefined, /*isIntersectionConstituent*/ !unionParent);
|
||||
if (!unionParent) {
|
||||
if (!related) {
|
||||
// Can't assign to a target individually - have to fallback to assigning to the _whole_ intersection (which forces normalization)
|
||||
return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors);
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
else {
|
||||
if (related) {
|
||||
return related;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unionParent && !result && targetIsOptional) {
|
||||
result = isRelatedTo(source, undefinedType);
|
||||
}
|
||||
if (unionParent && !result && reportErrors) {
|
||||
// The easiest way to get the right errors here is to un-defer (which may be costly)
|
||||
// If it turns out this is too costly too often, we can replicate the error handling logic within
|
||||
// typeRelatedToSomeType without the discriminatable type branch (as that requires a manifest union
|
||||
// type on which to hand discriminable properties, which we are expressly trying to avoid here)
|
||||
return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors);
|
||||
}
|
||||
}
|
||||
|
||||
function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary {
|
||||
const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp);
|
||||
const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp);
|
||||
@@ -13537,7 +13601,7 @@ namespace ts {
|
||||
return Ternary.False;
|
||||
}
|
||||
// If the target comes from a partial union prop, allow `undefined` in the target type
|
||||
const related = isRelatedTo(getTypeOfSourceProperty(sourceProp), addOptionality(getTypeOfSymbol(targetProp), !!(getCheckFlags(targetProp) & CheckFlags.Partial)), reportErrors);
|
||||
const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
|
||||
@@ -13649,9 +13713,6 @@ namespace ts {
|
||||
if (!(targetProp.flags & SymbolFlags.Prototype)) {
|
||||
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
|
||||
if (sourceProp && sourceProp !== targetProp) {
|
||||
if (isIgnoredJsxProperty(source, sourceProp, getTypeOfSymbol(targetProp))) {
|
||||
continue;
|
||||
}
|
||||
const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
@@ -13797,7 +13858,7 @@ namespace ts {
|
||||
function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary {
|
||||
let result = Ternary.True;
|
||||
for (const prop of getPropertiesOfObjectType(source)) {
|
||||
if (isIgnoredJsxProperty(source, prop, /*targetMemberType*/ undefined)) {
|
||||
if (isIgnoredJsxProperty(source, prop)) {
|
||||
continue;
|
||||
}
|
||||
// Skip over symbol-named members
|
||||
|
||||
@@ -3752,6 +3752,8 @@ namespace ts {
|
||||
extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in
|
||||
extendedContainersByFile?: Map<Symbol[]>; // Containers (other than the parent) which this symbol is aliased in
|
||||
variances?: VarianceFlags[]; // Alias symbol type argument variance cache
|
||||
deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type
|
||||
deferralParent?: Type; // Source union/intersection of a deferred type
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -3778,6 +3780,7 @@ namespace ts {
|
||||
ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type
|
||||
OptionalParameter = 1 << 14, // Optional parameter
|
||||
RestParameter = 1 << 15, // Rest parameter
|
||||
DeferredType = 1 << 16, // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType`
|
||||
Synthetic = SyntheticProperty | SyntheticMethod,
|
||||
Discriminant = HasNonUniformType | HasLiteralType,
|
||||
Partial = ReadPartial | WritePartial
|
||||
|
||||
Reference in New Issue
Block a user