Add unmeasurable variance kind for marking types whose variance result is unreliable (#30416)

* Add unmeasurable variance kind for marking types whose variance result is unreliable

* Remove now-unneeded nongeneric checks

* Add rule allowing `Readonly<any>` to be `any` instead of `{readonly [index: string]: any}`

* All Unmeasurable variances to still shortcut structural comparisons in some cases

* Separate unmeasurable from unreliable to reduce the impact of this change, for now

* Fix lint

* Remove Readonly<any> -> any callout

* Add fix for circularity error triggered by deep signature return type comparisons with `this` types
This commit is contained in:
Wesley Wigham
2019-05-03 14:42:17 -07:00
committed by GitHub
parent fc88a1c51a
commit b365e657d4
23 changed files with 1092 additions and 57 deletions

View File

@@ -678,6 +678,7 @@ namespace ts {
let _jsxNamespace: __String;
let _jsxFactoryEntity: EntityName | undefined;
let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
const subtypeRelation = createMap<RelationComparisonResult>();
const assignableRelation = createMap<RelationComparisonResult>();
@@ -12062,12 +12063,14 @@ namespace ts {
}
if (!ignoreReturnTypes) {
const targetReturnType = (target.declaration && isJSConstructor(target.declaration)) ?
// If a signature reolution is already in-flight, skip issuing a circularity error
// here and just use the `any` type directly
const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType : (target.declaration && isJSConstructor(target.declaration)) ?
getJSClassType(target.declaration.symbol)! : getReturnTypeOfSignature(target);
if (targetReturnType === voidType) {
return result;
}
const sourceReturnType = (source.declaration && isJSConstructor(source.declaration)) ?
const sourceReturnType = isResolvingReturnTypeOfSignature(source) ? anyType : (source.declaration && isJSConstructor(source.declaration)) ?
getJSClassType(source.declaration.symbol)! : getReturnTypeOfSignature(source);
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
@@ -12852,7 +12855,7 @@ namespace ts {
return result;
}
function typeArgumentsRelatedTo(sources: ReadonlyArray<Type> = emptyArray, targets: ReadonlyArray<Type> = emptyArray, variances: ReadonlyArray<Variance> = emptyArray, reportErrors: boolean): Ternary {
function typeArgumentsRelatedTo(sources: ReadonlyArray<Type> = emptyArray, targets: ReadonlyArray<Type> = emptyArray, variances: ReadonlyArray<VarianceFlags> = emptyArray, reportErrors: boolean): Ternary {
if (sources.length !== targets.length && relation === identityRelation) {
return Ternary.False;
}
@@ -12862,19 +12865,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,
@@ -13254,21 +13264,20 @@ namespace ts {
}
return Ternary.False;
function isNonGeneric(type: Type) {
// If we're already in identity relationship checking, we should use `isRelatedTo`
// to catch the `Maybe` from an excessively deep type (which we then assume means
// that the type could possibly contain a generic)
if (relation === identityRelation) {
return isRelatedTo(type, getPermissiveInstantiation(type)) === Ternary.True;
}
return isTypeIdenticalTo(type, getPermissiveInstantiation(type));
}
function relateVariances(sourceTypeArguments: ReadonlyArray<Type> | undefined, targetTypeArguments: ReadonlyArray<Type> | undefined, variances: Variance[]) {
function relateVariances(sourceTypeArguments: ReadonlyArray<Type> | undefined, targetTypeArguments: ReadonlyArray<Type> | undefined, variances: VarianceFlags[]) {
if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors)) {
return result;
}
const allowStructuralFallback = (targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances)) || isNonGeneric(source) || isNonGeneric(target);
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.
originalErrorInfo = undefined;
errorInfo = saveErrorInfo;
return undefined;
}
const allowStructuralFallback = targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances);
varianceCheckFailed = !allowStructuralFallback;
// The type arguments did not relate appropriately, but it may be because we have no variance
// information (in which case typeArgumentsRelatedTo defaulted to covariance for all type
@@ -13286,7 +13295,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
@@ -13298,6 +13307,20 @@ namespace ts {
}
}
function reportUnmeasurableMarkers(p: TypeParameter) {
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;
}
// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
// that S and T are contra-variant whereas X and Y are co-variant.
@@ -13306,7 +13329,9 @@ namespace ts {
getCombinedMappedTypeOptionality(source) <= getCombinedMappedTypeOptionality(target));
if (modifiersRelated) {
let result: Ternary;
if (result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) {
const targetConstraint = getConstraintTypeFromMappedType(target);
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);
}
@@ -13896,26 +13921,45 @@ 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<TCache extends { variances?: Variance[] }>(typeParameters: ReadonlyArray<TypeParameter> = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): Variance[] {
function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: ReadonlyArray<TypeParameter> = 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.
cache.variances = emptyArray;
variances = [];
for (const tp of typeParameters) {
let unmeasurable = false;
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;
}
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,
// And then fall back to a structural result.
assignableRelation.delete(covariantID);
assignableRelation.delete(contravariantID);
}
variances.push(variance);
}
@@ -13924,7 +13968,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;
@@ -13934,9 +13978,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<Type>, variances: Variance[]): boolean {
function hasCovariantVoidArgument(typeArguments: ReadonlyArray<Type>, 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;
}
}
@@ -15109,7 +15153,7 @@ namespace ts {
const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length;
const variances = getVariances((<TypeReference>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 {

View File

@@ -3735,7 +3735,7 @@ namespace ts {
specifierCache?: Map<string>; // 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<Symbol[]>; // 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,12 +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
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
@@ -4144,7 +4148,7 @@ namespace ts {
/* @internal */
instantiations: Map<TypeReference>; // Generic instantiation cache
/* @internal */
variances?: Variance[]; // Variance of each type parameter
variances?: VarianceFlags[]; // Variance of each type parameter
}
export interface TupleType extends GenericType {

View File

@@ -0,0 +1,32 @@
tests/cases/compiler/checkInfiniteExpansionTermination.ts(16,1): error TS2322: Type 'ISubject<Bar>' is not assignable to type 'IObservable<Foo>'.
Types of property 'n' are incompatible.
Type 'IObservable<Bar[]>' is not assignable to type 'IObservable<Foo[]>'.
Type 'Bar[]' is not assignable to type 'Foo[]'.
Property 'x' is missing in type 'Bar' but required in type 'Foo'.
==== tests/cases/compiler/checkInfiniteExpansionTermination.ts (1 errors) ====
// Regression test for #1002
// Before fix this code would cause infinite loop
interface IObservable<T> {
n: IObservable<T[]>; // Needed, must be T[]
}
// Needed
interface ISubject<T> extends IObservable<T> { }
interface Foo { x }
interface Bar { y }
var values: IObservable<Foo>;
var values2: ISubject<Bar>;
values = values2;
~~~~~~
!!! error TS2322: Type 'ISubject<Bar>' is not assignable to type 'IObservable<Foo>'.
!!! error TS2322: Types of property 'n' are incompatible.
!!! error TS2322: Type 'IObservable<Bar[]>' is not assignable to type 'IObservable<Foo[]>'.
!!! error TS2322: Type 'Bar[]' is not assignable to type 'Foo[]'.
!!! error TS2322: Property 'x' is missing in type 'Bar' but required in type 'Foo'.
!!! related TS2728 tests/cases/compiler/checkInfiniteExpansionTermination.ts:11:17: 'x' is declared here.

View File

@@ -19,12 +19,8 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(104,5): error TS2
'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
tests/cases/conformance/types/conditional/conditionalTypes1.ts(106,5): error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>'.
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<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
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'.
@@ -195,15 +191,11 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
~
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>'.
!!! 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<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
!!! 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<T>(x: keyof T, y: FunctionPropertyNames<T>, z: NonFunctionPropertyNames<T>) {

View File

@@ -0,0 +1,55 @@
tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(18,5): error TS2741: Property 'a' is missing in type 'Record<string, string>' but required in type 'Record2<"a", string>'.
tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(22,5): error TS2741: Property 'a' is missing in type 'Record2<string, string>' but required in type 'Record<"a", string>'.
tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(34,5): error TS2741: Property 'a' is missing in type 'Record<string, T>' but required in type 'Record2<"a", T>'.
tests/cases/compiler/consistentAliasVsNonAliasRecordBehavior.ts(38,5): error TS2741: Property 'a' is missing in type 'Record2<string, T>' 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
// Ref: https://github.com/Microsoft/TypeScript/issues/29698
type Record2<K extends keyof any, T> = {
[P in K]: T;
};
function defaultRecord(x: Record<'a', string>, y: Record<string, string>) {
x = y; // no error, but error expected.
}
function customRecord(x: Record2<'a', string>, y: Record2<string, string>) {
x = y; // no error, but error expected.
}
function mixed1(x: Record2<'a', string>, y: Record<string, string>) {
x = y; // error
~
!!! error TS2741: Property 'a' is missing in type 'Record<string, string>' but required in type 'Record2<"a", string>'.
}
function mixed2(x: Record<'a', string>, y: Record2<string, string>) {
x = y; // error
~
!!! error TS2741: Property 'a' is missing in type 'Record2<string, string>' but required in type 'Record<"a", string>'.
}
function defaultRecord2<T>(x: Record<'a', T>, y: Record<string, T>) {
x = y; // no error, but error expected.
}
function customRecord2<T>(x: Record2<'a', T>, y: Record2<string, T>) {
x = y; // no error, but error expected.
}
function mixed3<T>(x: Record2<'a', T>, y: Record<string, T>) {
x = y; // error
~
!!! error TS2741: Property 'a' is missing in type 'Record<string, T>' but required in type 'Record2<"a", T>'.
}
function mixed4<T>(x: Record<'a', T>, y: Record2<string, T>) {
x = y; // error
~
!!! error TS2741: Property 'a' is missing in type 'Record2<string, T>' but required in type 'Record<"a", T>'.
}

View File

@@ -0,0 +1,70 @@
//// [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
// Ref: https://github.com/Microsoft/TypeScript/issues/29698
type Record2<K extends keyof any, T> = {
[P in K]: T;
};
function defaultRecord(x: Record<'a', string>, y: Record<string, string>) {
x = y; // no error, but error expected.
}
function customRecord(x: Record2<'a', string>, y: Record2<string, string>) {
x = y; // no error, but error expected.
}
function mixed1(x: Record2<'a', string>, y: Record<string, string>) {
x = y; // error
}
function mixed2(x: Record<'a', string>, y: Record2<string, string>) {
x = y; // error
}
function defaultRecord2<T>(x: Record<'a', T>, y: Record<string, T>) {
x = y; // no error, but error expected.
}
function customRecord2<T>(x: Record2<'a', T>, y: Record2<string, T>) {
x = y; // no error, but error expected.
}
function mixed3<T>(x: Record2<'a', T>, y: Record<string, T>) {
x = y; // error
}
function mixed4<T>(x: Record<'a', T>, y: Record2<string, T>) {
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
// Ref: https://github.com/Microsoft/TypeScript/issues/29698
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
}

View File

@@ -0,0 +1,125 @@
=== 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
// Ref: https://github.com/Microsoft/TypeScript/issues/29698
type Record2<K extends keyof any, T> = {
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
>K : Symbol(K, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 4, 13))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 4, 33))
[P in K]: T;
>P : Symbol(P, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 5, 5))
>K : Symbol(K, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 4, 13))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 4, 33))
};
function defaultRecord(x: Record<'a', string>, y: Record<string, string>) {
>defaultRecord : Symbol(defaultRecord, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 6, 2))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 8, 23))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 8, 46))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
x = y; // no error, but error expected.
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 8, 23))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 8, 46))
}
function customRecord(x: Record2<'a', string>, y: Record2<string, string>) {
>customRecord : Symbol(customRecord, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 10, 1))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 12, 22))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 12, 46))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
x = y; // no error, but error expected.
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 12, 22))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 12, 46))
}
function mixed1(x: Record2<'a', string>, y: Record<string, string>) {
>mixed1 : Symbol(mixed1, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 14, 1))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 16, 16))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 16, 40))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
x = y; // error
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 16, 16))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 16, 40))
}
function mixed2(x: Record<'a', string>, y: Record2<string, string>) {
>mixed2 : Symbol(mixed2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 18, 1))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 20, 16))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 20, 39))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
x = y; // error
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 20, 16))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 20, 39))
}
function defaultRecord2<T>(x: Record<'a', T>, y: Record<string, T>) {
>defaultRecord2 : Symbol(defaultRecord2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 22, 1))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 24, 24))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 24, 27))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 24, 24))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 24, 45))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 24, 24))
x = y; // no error, but error expected.
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 24, 27))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 24, 45))
}
function customRecord2<T>(x: Record2<'a', T>, y: Record2<string, T>) {
>customRecord2 : Symbol(customRecord2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 26, 1))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 28, 23))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 28, 26))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 28, 23))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 28, 45))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 28, 23))
x = y; // no error, but error expected.
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 28, 26))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 28, 45))
}
function mixed3<T>(x: Record2<'a', T>, y: Record<string, T>) {
>mixed3 : Symbol(mixed3, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 30, 1))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 32, 16))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 32, 19))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 32, 16))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 32, 38))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 32, 16))
x = y; // error
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 32, 19))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 32, 38))
}
function mixed4<T>(x: Record<'a', T>, y: Record2<string, T>) {
>mixed4 : Symbol(mixed4, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 34, 1))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 36, 16))
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 36, 19))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 36, 16))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 36, 37))
>Record2 : Symbol(Record2, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 0, 0))
>T : Symbol(T, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 36, 16))
x = y; // error
>x : Symbol(x, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 36, 19))
>y : Symbol(y, Decl(consistentAliasVsNonAliasRecordBehavior.ts, 36, 37))
}

View File

@@ -0,0 +1,99 @@
=== 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
// Ref: https://github.com/Microsoft/TypeScript/issues/29698
type Record2<K extends keyof any, T> = {
>Record2 : Record2<K, T>
[P in K]: T;
};
function defaultRecord(x: Record<'a', string>, y: Record<string, string>) {
>defaultRecord : (x: Record<"a", string>, y: Record<string, string>) => void
>x : Record<"a", string>
>y : Record<string, string>
x = y; // no error, but error expected.
>x = y : Record<string, string>
>x : Record<"a", string>
>y : Record<string, string>
}
function customRecord(x: Record2<'a', string>, y: Record2<string, string>) {
>customRecord : (x: Record2<"a", string>, y: Record2<string, string>) => void
>x : Record2<"a", string>
>y : Record2<string, string>
x = y; // no error, but error expected.
>x = y : Record2<string, string>
>x : Record2<"a", string>
>y : Record2<string, string>
}
function mixed1(x: Record2<'a', string>, y: Record<string, string>) {
>mixed1 : (x: Record2<"a", string>, y: Record<string, string>) => void
>x : Record2<"a", string>
>y : Record<string, string>
x = y; // error
>x = y : Record<string, string>
>x : Record2<"a", string>
>y : Record<string, string>
}
function mixed2(x: Record<'a', string>, y: Record2<string, string>) {
>mixed2 : (x: Record<"a", string>, y: Record2<string, string>) => void
>x : Record<"a", string>
>y : Record2<string, string>
x = y; // error
>x = y : Record2<string, string>
>x : Record<"a", string>
>y : Record2<string, string>
}
function defaultRecord2<T>(x: Record<'a', T>, y: Record<string, T>) {
>defaultRecord2 : <T>(x: Record<"a", T>, y: Record<string, T>) => void
>x : Record<"a", T>
>y : Record<string, T>
x = y; // no error, but error expected.
>x = y : Record<string, T>
>x : Record<"a", T>
>y : Record<string, T>
}
function customRecord2<T>(x: Record2<'a', T>, y: Record2<string, T>) {
>customRecord2 : <T>(x: Record2<"a", T>, y: Record2<string, T>) => void
>x : Record2<"a", T>
>y : Record2<string, T>
x = y; // no error, but error expected.
>x = y : Record2<string, T>
>x : Record2<"a", T>
>y : Record2<string, T>
}
function mixed3<T>(x: Record2<'a', T>, y: Record<string, T>) {
>mixed3 : <T>(x: Record2<"a", T>, y: Record<string, T>) => void
>x : Record2<"a", T>
>y : Record<string, T>
x = y; // error
>x = y : Record<string, T>
>x : Record2<"a", T>
>y : Record<string, T>
}
function mixed4<T>(x: Record<'a', T>, y: Record2<string, T>) {
>mixed4 : <T>(x: Record<"a", T>, y: Record2<string, T>) => void
>x : Record<"a", T>
>y : Record2<string, T>
x = y; // error
>x = y : Record2<string, T>
>x : Record<"a", T>
>y : Record2<string, T>
}

View File

@@ -0,0 +1,52 @@
tests/cases/compiler/invariantGenericErrorElaboration.ts(3,7): error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Num>' is not assignable to type 'Constraint<Runtype<any>>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Constraint<Num>>' is not assignable to type 'Constraint<Constraint<Runtype<any>>>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Constraint<Constraint<Num>>>' is not assignable to type 'Constraint<Constraint<Constraint<Runtype<any>>>>'.
Type 'Constraint<Constraint<Runtype<any>>>' is not assignable to type 'Constraint<Constraint<Num>>'.
Types of property 'underlying' are incompatible.
Type 'Constraint<Runtype<any>>' is not assignable to type 'Constraint<Num>'.
tests/cases/compiler/invariantGenericErrorElaboration.ts(4,19): error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
==== tests/cases/compiler/invariantGenericErrorElaboration.ts (2 errors) ====
// Repro from #19746
const wat: Runtype<any> = Num;
~~~
!!! error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Num>' is not assignable to type 'Constraint<Runtype<any>>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Constraint<Num>>' is not assignable to type 'Constraint<Constraint<Runtype<any>>>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Constraint<Constraint<Num>>>' is not assignable to type 'Constraint<Constraint<Constraint<Runtype<any>>>>'.
!!! error TS2322: Type 'Constraint<Constraint<Runtype<any>>>' is not assignable to type 'Constraint<Constraint<Num>>'.
!!! error TS2322: Types of property 'underlying' are incompatible.
!!! error TS2322: Type 'Constraint<Runtype<any>>' is not assignable to type 'Constraint<Num>'.
!!! related TS2728 tests/cases/compiler/invariantGenericErrorElaboration.ts:12:3: 'tag' is declared here.
const Foo = Obj({ foo: Num })
~~~
!!! error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
!!! related TS6501 tests/cases/compiler/invariantGenericErrorElaboration.ts:17:34: The expected type comes from this index signature.
interface Runtype<A> {
constraint: Constraint<this>
witness: A
}
interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num
interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;
interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}

View File

@@ -6,8 +6,8 @@ const wat: Runtype<any> = Num;
>Num : Num
const Foo = Obj({ foo: Num })
>Foo : Obj<{ foo: Num; }>
>Obj({ foo: Num }) : Obj<{ foo: Num; }>
>Foo : any
>Obj({ foo: Num }) : any
>Obj : <O extends { [_: string]: Runtype<any>; }>(fields: O) => Obj<O>
>{ foo: Num } : { foo: Num; }
>foo : Num

View File

@@ -1,8 +1,6 @@
tests/cases/conformance/types/mapped/mappedTypes5.ts(6,9): error TS2322: Type 'Partial<T>' is not assignable to type 'Readonly<T>'.
tests/cases/conformance/types/mapped/mappedTypes5.ts(8,9): error TS2322: Type 'Partial<Readonly<T>>' is not assignable to type 'Readonly<T>'.
tests/cases/conformance/types/mapped/mappedTypes5.ts(9,9): error TS2322: Type 'Readonly<Partial<T>>' is not assignable to type 'Readonly<T>'.
Type 'Partial<T>' is not assignable to type 'T'.
'Partial<T>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
==== tests/cases/conformance/types/mapped/mappedTypes5.ts (3 errors) ====
@@ -21,8 +19,6 @@ tests/cases/conformance/types/mapped/mappedTypes5.ts(9,9): error TS2322: Type 'R
let b4: Readonly<T> = rp; // Error
~~
!!! error TS2322: Type 'Readonly<Partial<T>>' is not assignable to type 'Readonly<T>'.
!!! error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
!!! error TS2322: 'Partial<T>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
let c1: Partial<Readonly<T>> = p;
let c2: Partial<Readonly<T>> = r;
let c3: Partial<Readonly<T>> = pr;

View File

@@ -0,0 +1,27 @@
tests/cases/compiler/recursiveTypeComparison.ts(14,5): error TS2322: Type 'Observable<{}>' is not assignable to type 'Property<number>'.
Types of property 'needThisOne' are incompatible.
Type 'Observable<{}>' is not assignable to type 'Observable<number>'.
Type '{}' is not assignable to type 'number'.
==== tests/cases/compiler/recursiveTypeComparison.ts (1 errors) ====
// Before fix this would take an exceeding long time to complete (#1170)
interface Observable<T> {
// This member can't be of type T, Property<T>, or Observable<anything but T>
needThisOne: Observable<T>;
// Add more to make it slower
expo1: Property<T[]>; // 0.31 seconds in check
expo2: Property<T[]>; // 3.11 seconds
expo3: Property<T[]>; // 82.28 seconds
}
interface Property<T> extends Observable<T> { }
var p: Observable<{}>;
var stuck: Property<number> = p;
~~~~~
!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Property<number>'.
!!! error TS2322: Types of property 'needThisOne' are incompatible.
!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<number>'.
!!! error TS2322: Type '{}' is not assignable to type 'number'.

View File

@@ -0,0 +1,61 @@
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(5,1): error TS2741: Property 'a' is missing in type 'Required<{ b?: 1; x: 1; }>' but required in type 'Required<{ a?: 1; x: 1; }>'.
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(6,1): error TS2741: Property 'b' is missing in type 'Required<{ a?: 1; x: 1; }>' but required in type 'Required<{ b?: 1; x: 1; }>'.
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(8,3): error TS2339: Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(9,3): error TS2339: Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(18,1): error TS2322: Type 'Foo<{ b?: 1; x: 1; }>' is not assignable to type 'Foo<{ a?: 1; x: 1; }>'.
Types of property 'a' are incompatible.
Property 'a' is missing in type 'Required<{ b?: 1; x: 1; }>' but required in type 'Required<{ a?: 1; x: 1; }>'.
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(19,1): error TS2322: Type 'Foo<{ a?: 1; x: 1; }>' is not assignable to type 'Foo<{ b?: 1; x: 1; }>'.
Types of property 'a' are incompatible.
Property 'b' is missing in type 'Required<{ a?: 1; x: 1; }>' but required in type 'Required<{ b?: 1; x: 1; }>'.
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(21,6): error TS2339: Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts(22,6): error TS2339: Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
==== tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts (8 errors) ====
const a: Required<{ a?: 1; x: 1 }> = { a: 1, x: 1 };
const b: Required<{ b?: 1; x: 1 }> = { b: 1, x: 1 };
export let A = a;
export let B = b;
A = b; // Should Error
~
!!! error TS2741: Property 'a' is missing in type 'Required<{ b?: 1; x: 1; }>' but required in type 'Required<{ a?: 1; x: 1; }>'.
!!! related TS2728 tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts:1:21: 'a' is declared here.
B = a; // Should Error
~
!!! error TS2741: Property 'b' is missing in type 'Required<{ a?: 1; x: 1; }>' but required in type 'Required<{ b?: 1; x: 1; }>'.
!!! related TS2728 tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts:2:21: 'b' is declared here.
a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
~
!!! error TS2339: Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
b.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
~
!!! error TS2339: Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
interface Foo<T> {
a: Required<T>;
}
const aa: Foo<{ a?: 1; x: 1 }> = { a: { a: 1, x: 1 } };
const bb: Foo<{ b?: 1; x: 1 }> = { a: { b: 1, x: 1 } };
export let AA = aa;
export let BB = bb;
AA = bb; // Should Error
~~
!!! error TS2322: Type 'Foo<{ b?: 1; x: 1; }>' is not assignable to type 'Foo<{ a?: 1; x: 1; }>'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Property 'a' is missing in type 'Required<{ b?: 1; x: 1; }>' but required in type 'Required<{ a?: 1; x: 1; }>'.
!!! related TS2728 tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts:14:17: 'a' is declared here.
BB = aa; // Should Error
~~
!!! error TS2322: Type 'Foo<{ a?: 1; x: 1; }>' is not assignable to type 'Foo<{ b?: 1; x: 1; }>'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Property 'b' is missing in type 'Required<{ a?: 1; x: 1; }>' but required in type 'Required<{ b?: 1; x: 1; }>'.
!!! related TS2728 tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts:15:17: 'b' is declared here.
aa.a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
~
!!! error TS2339: Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
bb.a.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
~
!!! error TS2339: Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.

View File

@@ -0,0 +1,43 @@
//// [requiredMappedTypeModifierTrumpsVariance.ts]
const a: Required<{ a?: 1; x: 1 }> = { a: 1, x: 1 };
const b: Required<{ b?: 1; x: 1 }> = { b: 1, x: 1 };
export let A = a;
export let B = b;
A = b; // Should Error
B = a; // Should Error
a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
b.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
interface Foo<T> {
a: Required<T>;
}
const aa: Foo<{ a?: 1; x: 1 }> = { a: { a: 1, x: 1 } };
const bb: Foo<{ b?: 1; x: 1 }> = { a: { b: 1, x: 1 } };
export let AA = aa;
export let BB = bb;
AA = bb; // Should Error
BB = aa; // Should Error
aa.a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
bb.a.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
//// [requiredMappedTypeModifierTrumpsVariance.js]
"use strict";
exports.__esModule = true;
var a = { a: 1, x: 1 };
var b = { b: 1, x: 1 };
exports.A = a;
exports.B = b;
exports.A = b; // Should Error
exports.B = a; // Should Error
a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
b.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
var aa = { a: { a: 1, x: 1 } };
var bb = { a: { b: 1, x: 1 } };
exports.AA = aa;
exports.BB = bb;
exports.AA = bb; // Should Error
exports.BB = aa; // Should Error
aa.a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
bb.a.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.

View File

@@ -0,0 +1,92 @@
=== tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts ===
const a: Required<{ a?: 1; x: 1 }> = { a: 1, x: 1 };
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 5))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 19))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 26))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 38))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 44))
const b: Required<{ b?: 1; x: 1 }> = { b: 1, x: 1 };
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 5))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 19))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 26))
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 38))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 44))
export let A = a;
>A : Symbol(A, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 2, 10))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 5))
export let B = b;
>B : Symbol(B, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 3, 10))
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 5))
A = b; // Should Error
>A : Symbol(A, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 2, 10))
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 5))
B = a; // Should Error
>B : Symbol(B, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 3, 10))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 5))
a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 0, 5))
b.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 1, 5))
interface Foo<T> {
>Foo : Symbol(Foo, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 8, 4))
>T : Symbol(T, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 10, 14))
a: Required<T>;
>a : Symbol(Foo.a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 10, 18))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 10, 14))
}
const aa: Foo<{ a?: 1; x: 1 }> = { a: { a: 1, x: 1 } };
>aa : Symbol(aa, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 5))
>Foo : Symbol(Foo, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 8, 4))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 15))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 22))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 34))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 39))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 45))
const bb: Foo<{ b?: 1; x: 1 }> = { a: { b: 1, x: 1 } };
>bb : Symbol(bb, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 5))
>Foo : Symbol(Foo, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 8, 4))
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 15))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 22))
>a : Symbol(a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 34))
>b : Symbol(b, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 39))
>x : Symbol(x, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 45))
export let AA = aa;
>AA : Symbol(AA, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 15, 10))
>aa : Symbol(aa, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 5))
export let BB = bb;
>BB : Symbol(BB, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 16, 10))
>bb : Symbol(bb, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 5))
AA = bb; // Should Error
>AA : Symbol(AA, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 15, 10))
>bb : Symbol(bb, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 5))
BB = aa; // Should Error
>BB : Symbol(BB, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 16, 10))
>aa : Symbol(aa, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 5))
aa.a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
>aa.a : Symbol(Foo.a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 10, 18))
>aa : Symbol(aa, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 13, 5))
>a : Symbol(Foo.a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 10, 18))
bb.a.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
>bb.a : Symbol(Foo.a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 10, 18))
>bb : Symbol(bb, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 14, 5))
>a : Symbol(Foo.a, Decl(requiredMappedTypeModifierTrumpsVariance.ts, 10, 18))

View File

@@ -0,0 +1,109 @@
=== tests/cases/compiler/requiredMappedTypeModifierTrumpsVariance.ts ===
const a: Required<{ a?: 1; x: 1 }> = { a: 1, x: 1 };
>a : Required<{ a?: 1; x: 1; }>
>a : 1
>x : 1
>{ a: 1, x: 1 } : { a: 1; x: 1; }
>a : 1
>1 : 1
>x : 1
>1 : 1
const b: Required<{ b?: 1; x: 1 }> = { b: 1, x: 1 };
>b : Required<{ b?: 1; x: 1; }>
>b : 1
>x : 1
>{ b: 1, x: 1 } : { b: 1; x: 1; }
>b : 1
>1 : 1
>x : 1
>1 : 1
export let A = a;
>A : Required<{ a?: 1; x: 1; }>
>a : Required<{ a?: 1; x: 1; }>
export let B = b;
>B : Required<{ b?: 1; x: 1; }>
>b : Required<{ b?: 1; x: 1; }>
A = b; // Should Error
>A = b : Required<{ b?: 1; x: 1; }>
>A : Required<{ a?: 1; x: 1; }>
>b : Required<{ b?: 1; x: 1; }>
B = a; // Should Error
>B = a : Required<{ a?: 1; x: 1; }>
>B : Required<{ b?: 1; x: 1; }>
>a : Required<{ a?: 1; x: 1; }>
a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
>a.b : any
>a : Required<{ a?: 1; x: 1; }>
>b : any
b.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
>b.a : any
>b : Required<{ b?: 1; x: 1; }>
>a : any
interface Foo<T> {
a: Required<T>;
>a : Required<T>
}
const aa: Foo<{ a?: 1; x: 1 }> = { a: { a: 1, x: 1 } };
>aa : Foo<{ a?: 1; x: 1; }>
>a : 1
>x : 1
>{ a: { a: 1, x: 1 } } : { a: { a: 1; x: 1; }; }
>a : { a: 1; x: 1; }
>{ a: 1, x: 1 } : { a: 1; x: 1; }
>a : 1
>1 : 1
>x : 1
>1 : 1
const bb: Foo<{ b?: 1; x: 1 }> = { a: { b: 1, x: 1 } };
>bb : Foo<{ b?: 1; x: 1; }>
>b : 1
>x : 1
>{ a: { b: 1, x: 1 } } : { a: { b: 1; x: 1; }; }
>a : { b: 1; x: 1; }
>{ b: 1, x: 1 } : { b: 1; x: 1; }
>b : 1
>1 : 1
>x : 1
>1 : 1
export let AA = aa;
>AA : Foo<{ a?: 1; x: 1; }>
>aa : Foo<{ a?: 1; x: 1; }>
export let BB = bb;
>BB : Foo<{ b?: 1; x: 1; }>
>bb : Foo<{ b?: 1; x: 1; }>
AA = bb; // Should Error
>AA = bb : Foo<{ b?: 1; x: 1; }>
>AA : Foo<{ a?: 1; x: 1; }>
>bb : Foo<{ b?: 1; x: 1; }>
BB = aa; // Should Error
>BB = aa : Foo<{ a?: 1; x: 1; }>
>BB : Foo<{ b?: 1; x: 1; }>
>aa : Foo<{ a?: 1; x: 1; }>
aa.a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
>aa.a.b : any
>aa.a : Required<{ a?: 1; x: 1; }>
>aa : Foo<{ a?: 1; x: 1; }>
>a : Required<{ a?: 1; x: 1; }>
>b : any
bb.a.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
>bb.a.a : any
>bb.a : Required<{ b?: 1; x: 1; }>
>bb : Foo<{ b?: 1; x: 1; }>
>a : Required<{ b?: 1; x: 1; }>
>a : any

View File

@@ -62,9 +62,13 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(84,1): error TS2322: Type 'Fun
tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Comparer2<Dog>' is not assignable to type 'Comparer2<Animal>'.
Property 'dog' is missing in type 'Animal' but required in type 'Dog'.
tests/cases/compiler/strictFunctionTypesErrors.ts(126,1): error TS2322: Type 'Crate<Dog>' is not assignable to type 'Crate<Animal>'.
Type 'Animal' is not assignable to type 'Dog'.
Types of property 'onSetItem' are incompatible.
Type '(item: Dog) => void' is not assignable to type '(item: Animal) => void'.
Types of parameters 'item' and 'item' are incompatible.
Type 'Animal' is not assignable to type 'Dog'.
tests/cases/compiler/strictFunctionTypesErrors.ts(127,1): error TS2322: Type 'Crate<Animal>' is not assignable to type 'Crate<Dog>'.
Type 'Animal' is not assignable to type 'Dog'.
Types of property 'item' are incompatible.
Type 'Animal' is not assignable to type 'Dog'.
tests/cases/compiler/strictFunctionTypesErrors.ts(133,1): error TS2322: Type '(f: (x: Dog) => Dog) => void' is not assignable to type '(f: (x: Animal) => Animal) => void'.
Types of parameters 'f' and 'f' are incompatible.
Type 'Animal' is not assignable to type 'Dog'.
@@ -304,11 +308,15 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(155,5): error TS2322: Type '(c
animalCrate = dogCrate; // Error
~~~~~~~~~~~
!!! error TS2322: Type 'Crate<Dog>' is not assignable to type 'Crate<Animal>'.
!!! error TS2322: Type 'Animal' is not assignable to type 'Dog'.
!!! error TS2322: Types of property 'onSetItem' are incompatible.
!!! error TS2322: Type '(item: Dog) => void' is not assignable to type '(item: Animal) => void'.
!!! error TS2322: Types of parameters 'item' and 'item' are incompatible.
!!! error TS2322: Type 'Animal' is not assignable to type 'Dog'.
dogCrate = animalCrate; // Error
~~~~~~~~
!!! error TS2322: Type 'Crate<Animal>' is not assignable to type 'Crate<Dog>'.
!!! error TS2322: Type 'Animal' is not assignable to type 'Dog'.
!!! error TS2322: Types of property 'item' are incompatible.
!!! error TS2322: Type 'Animal' is not assignable to type 'Dog'.
// Verify that callback parameters are strictly checked

View File

@@ -0,0 +1,59 @@
//// [thisConditionalOnMethodReturnOfGenericInstance.ts]
class A<T> {
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
}
class B<T> extends A<T> {
method(): string | (this extends C ? undefined : null) {
return "";
}
}
class C<T = any> extends B<T> {
marker!: string;
}
const x = new C<{}>();
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
//// [thisConditionalOnMethodReturnOfGenericInstance.js]
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = /** @class */ (function () {
function A() {
}
return A;
}());
var B = /** @class */ (function (_super) {
__extends(B, _super);
function B() {
return _super !== null && _super.apply(this, arguments) || this;
}
B.prototype.method = function () {
return "";
};
return B;
}(A));
var C = /** @class */ (function (_super) {
__extends(C, _super);
function C() {
return _super !== null && _super.apply(this, arguments) || this;
}
return C;
}(B));
var x = new C();
var y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type

View File

@@ -0,0 +1,47 @@
=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts ===
class A<T> {
>A : Symbol(A, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 0))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
>unmeasurableUsage : Symbol(A.unmeasurableUsage, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 12))
>K : Symbol(K, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 1, 26))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 8))
>K : Symbol(K, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 1, 26))
}
class B<T> extends A<T> {
>B : Symbol(B, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 2, 1))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 8))
>A : Symbol(A, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 0, 0))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 8))
method(): string | (this extends C ? undefined : null) {
>method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))
return "";
}
}
class C<T = any> extends B<T> {
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 8))
>B : Symbol(B, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 2, 1))
>T : Symbol(T, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 8))
marker!: string;
>marker : Symbol(C.marker, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 10, 31))
}
const x = new C<{}>();
>x : Symbol(x, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 14, 5))
>C : Symbol(C, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 8, 1))
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
>y : Symbol(y, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 16, 5))
>x.method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))
>x : Symbol(x, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 14, 5))
>method : Symbol(B.method, Decl(thisConditionalOnMethodReturnOfGenericInstance.ts, 4, 25))

View File

@@ -0,0 +1,41 @@
=== tests/cases/compiler/thisConditionalOnMethodReturnOfGenericInstance.ts ===
class A<T> {
>A : A<T>
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
>unmeasurableUsage : { [K in keyof T]-?: T[K]; }
}
class B<T> extends A<T> {
>B : B<T>
>A : A<T>
method(): string | (this extends C ? undefined : null) {
>method : () => string | (this extends C<any> ? undefined : null)
>null : null
return "";
>"" : ""
}
}
class C<T = any> extends B<T> {
>C : C<T>
>B : B<T>
marker!: string;
>marker : string
}
const x = new C<{}>();
>x : C<{}>
>new C<{}>() : C<{}>
>C : typeof C
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type
>y : string | undefined
>x.method() : string | undefined
>x.method : () => string | undefined
>x : C<{}>
>method : () => string | undefined

View File

@@ -0,0 +1,39 @@
// 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
// Ref: https://github.com/Microsoft/TypeScript/issues/29698
type Record2<K extends keyof any, T> = {
[P in K]: T;
};
function defaultRecord(x: Record<'a', string>, y: Record<string, string>) {
x = y; // no error, but error expected.
}
function customRecord(x: Record2<'a', string>, y: Record2<string, string>) {
x = y; // no error, but error expected.
}
function mixed1(x: Record2<'a', string>, y: Record<string, string>) {
x = y; // error
}
function mixed2(x: Record<'a', string>, y: Record2<string, string>) {
x = y; // error
}
function defaultRecord2<T>(x: Record<'a', T>, y: Record<string, T>) {
x = y; // no error, but error expected.
}
function customRecord2<T>(x: Record2<'a', T>, y: Record2<string, T>) {
x = y; // no error, but error expected.
}
function mixed3<T>(x: Record2<'a', T>, y: Record<string, T>) {
x = y; // error
}
function mixed4<T>(x: Record<'a', T>, y: Record2<string, T>) {
x = y; // error
}

View File

@@ -0,0 +1,22 @@
const a: Required<{ a?: 1; x: 1 }> = { a: 1, x: 1 };
const b: Required<{ b?: 1; x: 1 }> = { b: 1, x: 1 };
export let A = a;
export let B = b;
A = b; // Should Error
B = a; // Should Error
a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
b.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.
interface Foo<T> {
a: Required<T>;
}
const aa: Foo<{ a?: 1; x: 1 }> = { a: { a: 1, x: 1 } };
const bb: Foo<{ b?: 1; x: 1 }> = { a: { b: 1, x: 1 } };
export let AA = aa;
export let BB = bb;
AA = bb; // Should Error
BB = aa; // Should Error
aa.a.b; // Property 'b' does not exist on type 'Required<{ a?: 1; x: 1; }>'.
bb.a.a; // Property 'a' does not exist on type 'Required<{ b?: 1; x: 1; }>'.

View File

@@ -0,0 +1,18 @@
// @strict: true
class A<T> {
unmeasurableUsage!: {[K in keyof T]-?: T[K]};
}
class B<T> extends A<T> {
method(): string | (this extends C ? undefined : null) {
return "";
}
}
class C<T = any> extends B<T> {
marker!: string;
}
const x = new C<{}>();
const y = x.method(); // usage flags `method` in `B` as circular and marks `y` as the error-any type