Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 13 commits into from May 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
106 changes: 75 additions & 31 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

20 changes: 12 additions & 8 deletions src/compiler/types.ts
Expand Up @@ -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 */
Expand Down Expand Up @@ -4131,20 +4131,24 @@ 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
export interface GenericType extends InterfaceType, TypeReference {
/* @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 {
Expand Down
@@ -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.

8 changes: 0 additions & 8 deletions tests/baselines/reference/conditionalTypes1.errors.txt
Expand Up @@ -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'.
Expand Down Expand Up @@ -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>) {
Expand Down
@@ -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>'.
}

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