Defer union or intersection property type normalization (#31486)

* Defer union or intersection property type normalization

* Accept moved span
This commit is contained in:
Wesley Wigham
2019-05-28 10:51:47 -07:00
committed by GitHub
parent 38f3b05cb1
commit e70f2af25d
15 changed files with 474 additions and 11 deletions

View File

@@ -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

View File

@@ -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