diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e7befc3a4dce0..7fd95944a82ad 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15189,12 +15189,14 @@ namespace ts { // is more predictable than other, interned types, which may or may not have an alias depending on // the order in which things were checked. if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol && - source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol && - !(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) { + source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) { + // Compute variances eagerly to ensure reliability flags are properly propagated. const variances = getAliasVariances(source.aliasSymbol); - const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent); - if (varianceResult !== undefined) { - return varianceResult; + if (!(containsMarkerType(source.aliasTypeArguments) || containsMarkerType(target.aliasTypeArguments))) { + const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent); + if (varianceResult !== undefined) { + return varianceResult; + } } } @@ -15383,7 +15385,7 @@ namespace ts { return Ternary.False; } if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target && - !(getObjectFlags(source) & ObjectFlags.MarkerType || getObjectFlags(target) & ObjectFlags.MarkerType)) { + !(containsMarkerType(getTypeArguments(source)) || containsMarkerType(getTypeArguments(target)))) { // We have type references to the same generic type, and the type references are not marker // type references (which are intended by be compared structurally). Obtain the variance // information for the type parameters and relate the type arguments accordingly. @@ -16157,21 +16159,24 @@ namespace ts { return false; } - // Return a type reference where the source type parameter is replaced with the target marker - // type, and flag the result as a marker type reference. - function getMarkerTypeReference(type: GenericType, source: TypeParameter, target: Type) { - const result = createTypeReference(type, map(type.typeParameters, t => t === source ? target : t)); - result.objectFlags |= ObjectFlags.MarkerType; - return result; + function containsMarkerType(types: readonly Type[] | undefined) { + return some(types, t => t === markerSuperType || t === markerSubType || t === markerOtherType); + } + + function getVariances(type: GenericType): VarianceFlags[] { + // Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters) + if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) { + return emptyArray; + } + return getVariancesWorker(type, type, createTypeReference); } function getAliasVariances(symbol: Symbol) { - const links = getSymbolLinks(symbol); - return getVariancesWorker(links.typeParameters, links, (_links, param, marker) => { - const type = getTypeAliasInstantiation(symbol, instantiateTypes(links.typeParameters!, makeUnaryTypeMapper(param, marker))); - type.aliasTypeArgumentsContainsMarker = true; - return type; - }); + return getVariancesWorker(symbol, getSymbolLinks(symbol), getTypeAliasInstantiation); + } + + function getTypeArgumentsWithMarker(typeParameters: TypeParameter[], source: TypeParameter, marker: TypeParameter) { + return map(typeParameters, makeUnaryTypeMapper(source, marker)); } // Return an array containing the variance of each type parameter. The variance is effectively @@ -16180,12 +16185,13 @@ namespace ts { // instantiations of the generic type for type arguments with known relations. The function // returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function // has been invoked recursively for the given generic type. - function getVariancesWorker(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] { + function getVariancesWorker(target: T, cache: GenericType | SymbolLinks, getInstance: (target: T, typeArguments: readonly Type[]) => Type): VarianceFlags[] { let variances = cache.variances; if (!variances) { // The emptyArray singleton is used to signal a recursive invocation. cache.variances = emptyArray; variances = []; + const typeParameters = cache.typeParameters || emptyArray; for (const tp of typeParameters) { let unmeasurable = false; let unreliable = false; @@ -16194,15 +16200,15 @@ namespace ts { // We first compare instantiations where the type parameter is replaced with // marker types that have a known subtype relationship. From this we can infer // invariance, covariance, contravariance or bivariance. - const typeWithSuper = createMarkerType(cache, tp, markerSuperType); - const typeWithSub = createMarkerType(cache, tp, markerSubType); + const typeWithSuper = getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerSuperType)); + const typeWithSub = getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerSubType)); let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) | (isTypeAssignableTo(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0); // If the instantiations appear to be related bivariantly it may be because the // type parameter is independent (i.e. it isn't witnessed anywhere in the generic // type). To determine this we compare instantiations where the type parameter is // replaced with marker types that are known to be unrelated. - if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(createMarkerType(cache, tp, markerOtherType), typeWithSuper)) { + if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerOtherType)), typeWithSuper)) { variance = VarianceFlags.Independent; } outofbandVarianceMarkerHandler = oldHandler; @@ -16221,14 +16227,6 @@ namespace ts { return variances; } - function getVariances(type: GenericType): VarianceFlags[] { - // Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters) - if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) { - return emptyArray; - } - return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference); - } - // Return true if the given type reference has a 'void' type argument for a covariant type parameter. // See comment at call in recursiveTypeRelatedTo for when this case matters. function hasCovariantVoidArgument(typeArguments: readonly Type[], variances: VarianceFlags[]): boolean { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 78f62f3013e8a..3483ad96f53bd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4271,7 +4271,6 @@ namespace ts { pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any) aliasSymbol?: Symbol; // Alias associated with type aliasTypeArguments?: readonly Type[]; // Alias type arguments (if any) - /* @internal */ aliasTypeArgumentsContainsMarker?: boolean; // Alias type arguments (if any) /* @internal */ permissiveInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type /* @internal */ @@ -4348,19 +4347,18 @@ namespace ts { ContainsSpread = 1 << 10, // Object literal contains spread operation ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type JsxAttributes = 1 << 12, // Jsx attributes type - MarkerType = 1 << 13, // Marker type used for variance probing - JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members - FreshLiteral = 1 << 15, // Fresh object literal - ArrayLiteral = 1 << 16, // Originates in an array literal - ObjectRestType = 1 << 17, // Originates in object rest declaration + JSLiteral = 1 << 13, // Object type declared in JS - disables errors on read/write of nonexisting members + FreshLiteral = 1 << 14, // Fresh object literal + ArrayLiteral = 1 << 15, // Originates in an array literal + ObjectRestType = 1 << 16, // Originates in object rest declaration /* @internal */ - PrimitiveUnion = 1 << 18, // Union of only primitive types + PrimitiveUnion = 1 << 17, // Union of only primitive types /* @internal */ - ContainsWideningType = 1 << 19, // Type is or contains undefined or null widening type + ContainsWideningType = 1 << 18, // Type is or contains undefined or null widening type /* @internal */ - ContainsObjectOrArrayLiteral = 1 << 20, // Type is or contains object literal type + ContainsObjectOrArrayLiteral = 1 << 19, // Type is or contains object literal type /* @internal */ - NonInferrableType = 1 << 21, // Type is or contains anyFunctionType or silentNeverType + NonInferrableType = 1 << 20, // Type is or contains anyFunctionType or silentNeverType ClassOrInterface = Class | Interface, /* @internal */ RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 94c856cfd0401..9133be3995c4b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2374,11 +2374,10 @@ declare namespace ts { ContainsSpread = 1024, ReverseMapped = 2048, JsxAttributes = 4096, - MarkerType = 8192, - JSLiteral = 16384, - FreshLiteral = 32768, - ArrayLiteral = 65536, - ObjectRestType = 131072, + JSLiteral = 8192, + FreshLiteral = 16384, + ArrayLiteral = 32768, + ObjectRestType = 65536, ClassOrInterface = 3, } export interface ObjectType extends Type { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 433217ee422c7..5c96188b8e958 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2374,11 +2374,10 @@ declare namespace ts { ContainsSpread = 1024, ReverseMapped = 2048, JsxAttributes = 4096, - MarkerType = 8192, - JSLiteral = 16384, - FreshLiteral = 32768, - ArrayLiteral = 65536, - ObjectRestType = 131072, + JSLiteral = 8192, + FreshLiteral = 16384, + ArrayLiteral = 32768, + ObjectRestType = 65536, ClassOrInterface = 3, } export interface ObjectType extends Type {