From 5e624e88b802bafb10e034ae4bf22d9fb811c770 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 29 Apr 2019 13:35:24 -0700 Subject: [PATCH] Separate unmeasurable from unreliable to reduce the impact of this change, for now --- src/compiler/checker.ts | 92 ++++++------- src/compiler/types.ts | 21 +-- .../reference/conditionalTypes1.errors.txt | 8 -- ...ntAliasVsNonAliasRecordBehavior.errors.txt | 54 ++++++++ ...consistentAliasVsNonAliasRecordBehavior.js | 68 ++++++++++ ...stentAliasVsNonAliasRecordBehavior.symbols | 124 ++++++++++++++++++ ...sistentAliasVsNonAliasRecordBehavior.types | 98 ++++++++++++++ ...consistentAliasVsNonAliasRecordBehavior.ts | 38 ++++++ 8 files changed, 442 insertions(+), 61 deletions(-) create mode 100644 tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.errors.txt create mode 100644 tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.js create mode 100644 tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.symbols create mode 100644 tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.types create mode 100644 tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2e3d997fc95a8..3c285cf779c53 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -680,7 +680,7 @@ namespace ts { let _jsxNamespace: __String; let _jsxFactoryEntity: EntityName | undefined; - let unmeasurableMarkerHandler: (() => void) | undefined; + let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined; const subtypeRelation = createMap(); const assignableRelation = createMap(); @@ -12809,7 +12809,7 @@ namespace ts { return result; } - function typeArgumentsRelatedTo(sources: ReadonlyArray = emptyArray, targets: ReadonlyArray = emptyArray, variances: ReadonlyArray = emptyArray, reportErrors: boolean): Ternary { + function typeArgumentsRelatedTo(sources: ReadonlyArray = emptyArray, targets: ReadonlyArray = emptyArray, variances: ReadonlyArray = emptyArray, reportErrors: boolean): Ternary { if (sources.length !== targets.length && relation === identityRelation) { return Ternary.False; } @@ -12819,19 +12819,26 @@ namespace ts { // When variance information isn't available we default to covariance. This happens // in the process of computing variance information for recursive types and when // comparing 'this' type arguments. - const variance = i < variances.length ? variances[i] : Variance.Covariant; + const varianceFlags = i < variances.length ? variances[i] : VarianceFlags.Covariant; + const variance = varianceFlags & VarianceFlags.VarianceMask; // We ignore arguments for independent type parameters (because they're never witnessed). - if (variance !== Variance.Independent) { + if (variance !== VarianceFlags.Independent) { const s = sources[i]; const t = targets[i]; let related = Ternary.True; - if (variance === Variance.Covariant) { + if (varianceFlags & VarianceFlags.Unmeasurable) { + // Even an `Unmeasurable` variance works out without a structural check if the source and target are _identical_. + // We can't simply assume invariance, because `Unmeasurable` marks nonlinear relations, for example, a relation tained by + // the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be) + related = relation === identityRelation ? isRelatedTo(s, t, /*reportErrors*/ false) : compareTypesIdentical(s, t); + } + else if (variance === VarianceFlags.Covariant) { related = isRelatedTo(s, t, reportErrors); } - else if (variance === Variance.Contravariant) { + else if (variance === VarianceFlags.Contravariant) { related = isRelatedTo(t, s, reportErrors); } - else if (variance === Variance.Bivariant) { + else if (variance === VarianceFlags.Bivariant) { // In the bivariant case we first compare contravariantly without reporting // errors. Then, if that doesn't succeed, we compare covariantly with error // reporting. Thus, error elaboration will be based on the the covariant check, @@ -12841,22 +12848,6 @@ namespace ts { related = isRelatedTo(s, t, reportErrors); } } - else if (variance === Variance.Unmeasurable) { - if (isTypeAny(s) || isTypeAny(t)) { - // If the `s` or `t` type is `any`, even if the structural desugaring says that the types _shouldn't_ - // be relatable, it's very surprising if they're not - take `Component` to `Component` - even - // if the `any` position is unmeasurable, it's incredibly surprising that such a reference wouldn't check out. - // (In fact, any nonlinear relation involving `any` is usually surprising) Even if it's potentially not correct - // according to our underlying structural rules, we persist that assumption here. - related = Ternary.True; - } - else { - // Even an `Unmeasurable` variance works out without a structural check if the source and target are _identical_. - // We can't simply assume invariance, because `Unmeasurable` marks nonlinear relations, for example, a relation tained by - // the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be) - related = relation === identityRelation ? isRelatedTo(s, t, /*reportErrors*/ false) : compareTypesIdentical(s, t); - } - } else { // In the invariant case we first compare covariantly, and only when that // succeeds do we proceed to compare contravariantly. Thus, error elaboration @@ -13214,12 +13205,12 @@ namespace ts { } return Ternary.False; - function relateVariances(sourceTypeArguments: ReadonlyArray | undefined, targetTypeArguments: ReadonlyArray | undefined, variances: Variance[]) { + function relateVariances(sourceTypeArguments: ReadonlyArray | undefined, targetTypeArguments: ReadonlyArray | undefined, variances: VarianceFlags[]) { if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors)) { return result; } - if (some(variances, v => v === Variance.Unmeasurable)) { - // If some type parameter was `Unmeasurable`, and we couldn't pass by assuming it was invariant, then we + if (some(variances, v => !!(v & VarianceFlags.AllowsStructuralFallback))) { + // If some type parameter was `Unmeasurable` or `Unreliable`, and we couldn't pass by assuming it was identical, then we // have to allow a structural fallback check // We elide the variance-based error elaborations, since those might not be too helpful, since we'll potentially // be assuming identity of the type parameter. @@ -13245,7 +13236,7 @@ namespace ts { // reveal the reason). // We can switch on `reportErrors` here, since varianceCheckFailed guarantees we return `False`, // we can return `False` early here to skip calculating the structural error message we don't need. - if (varianceCheckFailed && !(reportErrors && some(variances, v => v === Variance.Invariant))) { + if (varianceCheckFailed && !(reportErrors && some(variances, v => (v & VarianceFlags.VarianceMask) === VarianceFlags.Invariant))) { return Ternary.False; } // We remember the original error information so we can restore it in case the structural @@ -13258,8 +13249,15 @@ namespace ts { } function reportUnmeasurableMarkers(p: TypeParameter) { - if (unmeasurableMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { - unmeasurableMarkerHandler(); + if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { + outofbandVarianceMarkerHandler(/*onlyUnreliable*/false); + } + return p; + } + + function reportUnreliableMarkers(p: TypeParameter) { + if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) { + outofbandVarianceMarkerHandler(/*onlyUnreliable*/true); } return p; } @@ -13273,7 +13271,7 @@ namespace ts { if (modifiersRelated) { let result: Ternary; const targetConstraint = getConstraintTypeFromMappedType(target); - const sourceConstraint = instantiateType(getConstraintTypeFromMappedType(source), reportUnmeasurableMarkers); + const sourceConstraint = instantiateType(getConstraintTypeFromMappedType(source), getCombinedMappedTypeOptionality(source) < 0 ? reportUnmeasurableMarkers : reportUnreliableMarkers); if (result = isRelatedTo(targetConstraint, sourceConstraint, reportErrors)) { const mapper = createTypeMapper([getTypeParameterFromMappedType(source)], [getTypeParameterFromMappedType(target)]); return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(source), mapper), getTemplateTypeFromMappedType(target), reportErrors); @@ -13747,7 +13745,7 @@ 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: ReadonlyArray = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): Variance[] { + function getVariancesWorker(typeParameters: ReadonlyArray = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] { let variances = cache.variances; if (!variances) { // The emptyArray singleton is used to signal a recursive invocation. @@ -13755,25 +13753,31 @@ namespace ts { variances = []; for (const tp of typeParameters) { let unmeasurable = false; - const oldHandler = unmeasurableMarkerHandler; - unmeasurableMarkerHandler = () => unmeasurable = true; + let unreliable = false; + const oldHandler = outofbandVarianceMarkerHandler; + outofbandVarianceMarkerHandler = (onlyUnreliable) => onlyUnreliable ? unreliable = true : unmeasurable = true; // 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); - let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : 0) | - (isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : 0); + 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 === Variance.Bivariant && isTypeAssignableTo(createMarkerType(cache, tp, markerOtherType), typeWithSuper)) { - variance = Variance.Independent; + if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(createMarkerType(cache, tp, markerOtherType), typeWithSuper)) { + variance = VarianceFlags.Independent; } - unmeasurableMarkerHandler = oldHandler; - if (unmeasurable) { - variance = Variance.Unmeasurable; + outofbandVarianceMarkerHandler = oldHandler; + if (unmeasurable || unreliable) { + if (unmeasurable) { + variance |= VarianceFlags.Unmeasurable; + } + if (unreliable) { + variance |= VarianceFlags.Unreliable; + } const covariantID = getRelationKey(typeWithSub, typeWithSuper, assignableRelation); const contravariantID = getRelationKey(typeWithSuper, typeWithSub, assignableRelation); // We delete the results of these checks, as we want them to actually be run, see the `Unmeasurable` variance we cache, @@ -13788,7 +13792,7 @@ namespace ts { return variances; } - function getVariances(type: GenericType): Variance[] { + 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; @@ -13798,9 +13802,9 @@ namespace ts { // 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: ReadonlyArray, variances: Variance[]): boolean { + function hasCovariantVoidArgument(typeArguments: ReadonlyArray, variances: VarianceFlags[]): boolean { for (let i = 0; i < variances.length; i++) { - if (variances[i] === Variance.Covariant && typeArguments[i].flags & TypeFlags.Void) { + if ((variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Covariant && typeArguments[i].flags & TypeFlags.Void) { return true; } } @@ -14973,7 +14977,7 @@ namespace ts { const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length; const variances = getVariances((source).target); for (let i = 0; i < count; i++) { - if (i < variances.length && variances[i] === Variance.Contravariant) { + if (i < variances.length && (variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Contravariant) { inferFromContravariantTypes(sourceTypes[i], targetTypes[i]); } else { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 82a1d22046f5e..ba9d1a2016c1a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3735,7 +3735,7 @@ namespace ts { specifierCache?: Map; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in extendedContainersByFile?: Map; // Containers (other than the parent) which this symbol is aliased in - variances?: Variance[]; // Alias symbol type argument variance cache + variances?: VarianceFlags[]; // Alias symbol type argument variance cache } /* @internal */ @@ -4131,13 +4131,16 @@ namespace ts { } /* @internal */ - export const enum Variance { - Invariant = 0, // Neither covariant nor contravariant - Covariant = 1, // Covariant - Contravariant = 2, // Contravariant - Bivariant = 3, // Both covariant and contravariant - Independent = 4, // Unwitnessed type parameter - Unmeasurable = 5, // Variance result is unusable - relationship relies on structural comparisons which are not reflected in generic relationships + export const enum VarianceFlags { + Invariant = 0, // Neither covariant nor contravariant + Covariant = 1 << 0, // Covariant + Contravariant = 1 << 1, // Contravariant + Bivariant = Covariant | Contravariant, // Both covariant and contravariant + Independent = 1 << 2, // Unwitnessed type parameter + VarianceMask = Invariant | Covariant | Contravariant | Independent, // Mask containing all measured variances without the unmeasurable flag + Unmeasurable = 1 << 3, // Variance result is unusable - relationship relies on structural comparisons which are not reflected in generic relationships + Unreliable = 1 << 4, // Variance result is unreliable - relationship relies on structural comparisons which are not reflected in generic relationships + AllowsStructuralFallback = Unmeasurable | Unreliable, } // Generic class and interface types @@ -4145,7 +4148,7 @@ namespace ts { /* @internal */ instantiations: Map; // Generic instantiation cache /* @internal */ - variances?: Variance[]; // Variance of each type parameter + variances?: VarianceFlags[]; // Variance of each type parameter } export interface TupleType extends GenericType { diff --git a/tests/baselines/reference/conditionalTypes1.errors.txt b/tests/baselines/reference/conditionalTypes1.errors.txt index ec35f89161589..62239eca7b821 100644 --- a/tests/baselines/reference/conditionalTypes1.errors.txt +++ b/tests/baselines/reference/conditionalTypes1.errors.txt @@ -17,12 +17,8 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(103,5): error TS2 tests/cases/conformance/types/conditional/conditionalTypes1.ts(104,5): error TS2322: Type 'Pick' is not assignable to type 'T'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(106,5): error TS2322: Type 'Pick' is not assignable to type 'Pick'. Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'. - Type 'keyof T' is not assignable to type 'never'. - Type 'string | number | symbol' is not assignable to type 'never'. - Type 'string' is not assignable to type 'never'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(108,5): error TS2322: Type 'Pick' is not assignable to type 'Pick'. Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'. - Type 'keyof T' is not assignable to type 'never'. tests/cases/conformance/types/conditional/conditionalTypes1.ts(114,5): error TS2322: Type 'keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'. Type 'string | number | symbol' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'. Type 'string' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'. @@ -187,15 +183,11 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS ~ !!! error TS2322: Type 'Pick' is not assignable to type 'Pick'. !!! error TS2322: Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'. -!!! error TS2322: Type 'keyof T' is not assignable to type 'never'. -!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'never'. -!!! error TS2322: Type 'string' is not assignable to type 'never'. z = x; z = y; // Error ~ !!! error TS2322: Type 'Pick' is not assignable to type 'Pick'. !!! error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'. -!!! error TS2322: Type 'keyof T' is not assignable to type 'never'. } function f8(x: keyof T, y: FunctionPropertyNames, z: NonFunctionPropertyNames) { diff --git a/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.errors.txt b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.errors.txt new file mode 100644 index 0000000000000..d139a4261e575 --- /dev/null +++ b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.errors.txt @@ -0,0 +1,54 @@ +tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(17,5): error TS2741: Property 'a' is missing in type 'Record' but required in type 'Record2<"a", string>'. +tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(21,5): error TS2741: Property 'a' is missing in type 'Record2' but required in type 'Record<"a", string>'. +tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(33,5): error TS2741: Property 'a' is missing in type 'Record' but required in type 'Record2<"a", T>'. +tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(37,5): error TS2741: Property 'a' is missing in type 'Record2' but required in type 'Record<"a", T>'. + + +==== tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts (4 errors) ==== + // TODO: FIXME: All the below cases labeled `no error` _should be an error_, and are only prevented from so being + // by incorrect variance-based relationships + + type Record2 = { + [P in K]: T; + }; + + function defaultRecord(x: Record<'a', string>, y: Record) { + x = y; // no error, but error expected. + } + + function customRecord(x: Record2<'a', string>, y: Record2) { + x = y; // no error, but error expected. + } + + function mixed1(x: Record2<'a', string>, y: Record) { + x = y; // error + ~ +!!! error TS2741: Property 'a' is missing in type 'Record' but required in type 'Record2<"a", string>'. + } + + function mixed2(x: Record<'a', string>, y: Record2) { + x = y; // error + ~ +!!! error TS2741: Property 'a' is missing in type 'Record2' but required in type 'Record<"a", string>'. + } + + function defaultRecord2(x: Record<'a', T>, y: Record) { + x = y; // no error, but error expected. + } + + function customRecord2(x: Record2<'a', T>, y: Record2) { + x = y; // no error, but error expected. + } + + function mixed3(x: Record2<'a', T>, y: Record) { + x = y; // error + ~ +!!! error TS2741: Property 'a' is missing in type 'Record' but required in type 'Record2<"a", T>'. + } + + function mixed4(x: Record<'a', T>, y: Record2) { + x = y; // error + ~ +!!! error TS2741: Property 'a' is missing in type 'Record2' but required in type 'Record<"a", T>'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.js b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.js new file mode 100644 index 0000000000000..60c1c738bb663 --- /dev/null +++ b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.js @@ -0,0 +1,68 @@ +//// [consistentAliasVsNonAliasRecordBehavior.ts] +// TODO: FIXME: All the below cases labeled `no error` _should be an error_, and are only prevented from so being +// by incorrect variance-based relationships + +type Record2 = { + [P in K]: T; +}; + +function defaultRecord(x: Record<'a', string>, y: Record) { + x = y; // no error, but error expected. +} + +function customRecord(x: Record2<'a', string>, y: Record2) { + x = y; // no error, but error expected. +} + +function mixed1(x: Record2<'a', string>, y: Record) { + x = y; // error +} + +function mixed2(x: Record<'a', string>, y: Record2) { + x = y; // error +} + +function defaultRecord2(x: Record<'a', T>, y: Record) { + x = y; // no error, but error expected. +} + +function customRecord2(x: Record2<'a', T>, y: Record2) { + x = y; // no error, but error expected. +} + +function mixed3(x: Record2<'a', T>, y: Record) { + x = y; // error +} + +function mixed4(x: Record<'a', T>, y: Record2) { + x = y; // error +} + + +//// [consistentAliasVsNonAliasRecordBehavior.js] +// TODO: FIXME: All the below cases labeled `no error` _should be an error_, and are only prevented from so being +// by incorrect variance-based relationships +function defaultRecord(x, y) { + x = y; // no error, but error expected. +} +function customRecord(x, y) { + x = y; // no error, but error expected. +} +function mixed1(x, y) { + x = y; // error +} +function mixed2(x, y) { + x = y; // error +} +function defaultRecord2(x, y) { + x = y; // no error, but error expected. +} +function customRecord2(x, y) { + x = y; // no error, but error expected. +} +function mixed3(x, y) { + x = y; // error +} +function mixed4(x, y) { + x = y; // error +} diff --git a/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.symbols b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.symbols new file mode 100644 index 0000000000000..77b5c2f2ba64b --- /dev/null +++ b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.symbols @@ -0,0 +1,124 @@ +=== tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts === +// TODO: FIXME: All the below cases labeled `no error` _should be an error_, and are only prevented from so being +// by incorrect variance-based relationships + +type Record2 = { +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) +>K : Symbol(K, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 3, 13)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 3, 33)) + + [P in K]: T; +>P : Symbol(P, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 4, 5)) +>K : Symbol(K, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 3, 13)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 3, 33)) + +}; + +function defaultRecord(x: Record<'a', string>, y: Record) { +>defaultRecord : Symbol(defaultRecord, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 5, 2)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 7, 23)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 7, 46)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + x = y; // no error, but error expected. +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 7, 23)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 7, 46)) +} + +function customRecord(x: Record2<'a', string>, y: Record2) { +>customRecord : Symbol(customRecord, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 9, 1)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 11, 22)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 11, 46)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) + + x = y; // no error, but error expected. +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 11, 22)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 11, 46)) +} + +function mixed1(x: Record2<'a', string>, y: Record) { +>mixed1 : Symbol(mixed1, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 13, 1)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 15, 16)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 15, 40)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + x = y; // error +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 15, 16)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 15, 40)) +} + +function mixed2(x: Record<'a', string>, y: Record2) { +>mixed2 : Symbol(mixed2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 17, 1)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 19, 16)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 19, 39)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) + + x = y; // error +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 19, 16)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 19, 39)) +} + +function defaultRecord2(x: Record<'a', T>, y: Record) { +>defaultRecord2 : Symbol(defaultRecord2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 21, 1)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 23, 24)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 23, 27)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 23, 24)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 23, 45)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 23, 24)) + + x = y; // no error, but error expected. +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 23, 27)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 23, 45)) +} + +function customRecord2(x: Record2<'a', T>, y: Record2) { +>customRecord2 : Symbol(customRecord2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 25, 1)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 27, 23)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 27, 26)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 27, 23)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 27, 45)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 27, 23)) + + x = y; // no error, but error expected. +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 27, 26)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 27, 45)) +} + +function mixed3(x: Record2<'a', T>, y: Record) { +>mixed3 : Symbol(mixed3, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 29, 1)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 31, 16)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 31, 19)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 31, 16)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 31, 38)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 31, 16)) + + x = y; // error +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 31, 19)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 31, 38)) +} + +function mixed4(x: Record<'a', T>, y: Record2) { +>mixed4 : Symbol(mixed4, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 33, 1)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 35, 16)) +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 35, 19)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 35, 16)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 35, 37)) +>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0)) +>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 35, 16)) + + x = y; // error +>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 35, 19)) +>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 35, 37)) +} + diff --git a/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.types b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.types new file mode 100644 index 0000000000000..07e4d5142442f --- /dev/null +++ b/tests/baselines/reference/consistentAliasVsNonAliasRecordBehavior.types @@ -0,0 +1,98 @@ +=== tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts === +// TODO: FIXME: All the below cases labeled `no error` _should be an error_, and are only prevented from so being +// by incorrect variance-based relationships + +type Record2 = { +>Record2 : Record2 + + [P in K]: T; +}; + +function defaultRecord(x: Record<'a', string>, y: Record) { +>defaultRecord : (x: Record<"a", string>, y: Record) => void +>x : Record<"a", string> +>y : Record + + x = y; // no error, but error expected. +>x = y : Record +>x : Record<"a", string> +>y : Record +} + +function customRecord(x: Record2<'a', string>, y: Record2) { +>customRecord : (x: Record2<"a", string>, y: Record2) => void +>x : Record2<"a", string> +>y : Record2 + + x = y; // no error, but error expected. +>x = y : Record2 +>x : Record2<"a", string> +>y : Record2 +} + +function mixed1(x: Record2<'a', string>, y: Record) { +>mixed1 : (x: Record2<"a", string>, y: Record) => void +>x : Record2<"a", string> +>y : Record + + x = y; // error +>x = y : Record +>x : Record2<"a", string> +>y : Record +} + +function mixed2(x: Record<'a', string>, y: Record2) { +>mixed2 : (x: Record<"a", string>, y: Record2) => void +>x : Record<"a", string> +>y : Record2 + + x = y; // error +>x = y : Record2 +>x : Record<"a", string> +>y : Record2 +} + +function defaultRecord2(x: Record<'a', T>, y: Record) { +>defaultRecord2 : (x: Record<"a", T>, y: Record) => void +>x : Record<"a", T> +>y : Record + + x = y; // no error, but error expected. +>x = y : Record +>x : Record<"a", T> +>y : Record +} + +function customRecord2(x: Record2<'a', T>, y: Record2) { +>customRecord2 : (x: Record2<"a", T>, y: Record2) => void +>x : Record2<"a", T> +>y : Record2 + + x = y; // no error, but error expected. +>x = y : Record2 +>x : Record2<"a", T> +>y : Record2 +} + +function mixed3(x: Record2<'a', T>, y: Record) { +>mixed3 : (x: Record2<"a", T>, y: Record) => void +>x : Record2<"a", T> +>y : Record + + x = y; // error +>x = y : Record +>x : Record2<"a", T> +>y : Record +} + +function mixed4(x: Record<'a', T>, y: Record2) { +>mixed4 : (x: Record<"a", T>, y: Record2) => void +>x : Record<"a", T> +>y : Record2 + + x = y; // error +>x = y : Record2 +>x : Record<"a", T> +>y : Record2 +} + diff --git a/tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts b/tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts new file mode 100644 index 0000000000000..970084a19a115 --- /dev/null +++ b/tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts @@ -0,0 +1,38 @@ +// TODO: FIXME: All the below cases labeled `no error` _should be an error_, and are only prevented from so being +// by incorrect variance-based relationships + +type Record2 = { + [P in K]: T; +}; + +function defaultRecord(x: Record<'a', string>, y: Record) { + x = y; // no error, but error expected. +} + +function customRecord(x: Record2<'a', string>, y: Record2) { + x = y; // no error, but error expected. +} + +function mixed1(x: Record2<'a', string>, y: Record) { + x = y; // error +} + +function mixed2(x: Record<'a', string>, y: Record2) { + x = y; // error +} + +function defaultRecord2(x: Record<'a', T>, y: Record) { + x = y; // no error, but error expected. +} + +function customRecord2(x: Record2<'a', T>, y: Record2) { + x = y; // no error, but error expected. +} + +function mixed3(x: Record2<'a', T>, y: Record) { + x = y; // error +} + +function mixed4(x: Record<'a', T>, y: Record2) { + x = y; // error +}