Merge pull request #27357 from Microsoft/fixBivariantInferences

Make contravariant inferences only from pure contravariant positions
This commit is contained in:
Anders Hejlsberg 2018-09-26 14:03:59 -07:00 committed by GitHub
commit d7219b21c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 97 additions and 3 deletions

View file

@ -13462,6 +13462,7 @@ namespace ts {
let symbolStack: Symbol[];
let visited: Map<boolean>;
let contravariant = false;
let bivariant = false;
let propagationType: Type;
let allowComplexConstraintInference = true;
inferFromTypes(originalSource, originalTarget);
@ -13548,11 +13549,13 @@ namespace ts {
}
if (priority === inference.priority) {
const candidate = propagationType || source;
if (contravariant) {
inference.contraCandidates = append(inference.contraCandidates, candidate);
// We make contravariant inferences only if we are in a pure contravariant position,
// i.e. only if we have not descended into a bivariant position.
if (contravariant && !bivariant) {
inference.contraCandidates = appendIfUnique(inference.contraCandidates, candidate);
}
else {
inference.candidates = append(inference.candidates, candidate);
inference.candidates = appendIfUnique(inference.candidates, candidate);
}
}
if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
@ -13800,7 +13803,12 @@ namespace ts {
function inferFromSignature(source: Signature, target: Signature, skipParameters: boolean) {
if (!skipParameters) {
const saveBivariant = bivariant;
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
// Once we descend into a bivariant signature we remain bivariant for all nested inferences
bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor;
forEachMatchingParameterType(source, target, inferFromContravariantTypes);
bivariant = saveBivariant;
}
const sourceTypePredicate = getTypePredicateOfSignature(source);
const targetTypePredicate = getTypePredicateOfSignature(target);

View file

@ -0,0 +1,17 @@
//// [bivariantInferences.ts]
// Repro from #27337
interface Array<T> {
equalsShallow<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>): boolean;
}
declare const a: (string | number)[] | null[] | undefined[] | {}[];
declare const b: (string | number)[] | null[] | undefined[] | {}[];
let x = a.equalsShallow(b);
//// [bivariantInferences.js]
"use strict";
// Repro from #27337
var x = a.equalsShallow(b);

View file

@ -0,0 +1,31 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts ===
// Repro from #27337
interface Array<T> {
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(bivariantInferences.ts, 0, 0))
>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(bivariantInferences.ts, 2, 16))
equalsShallow<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>): boolean;
>equalsShallow : Symbol(Array.equalsShallow, Decl(bivariantInferences.ts, 2, 20))
>T : Symbol(T, Decl(bivariantInferences.ts, 3, 18))
>this : Symbol(this, Decl(bivariantInferences.ts, 3, 21))
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(bivariantInferences.ts, 3, 18))
>other : Symbol(other, Decl(bivariantInferences.ts, 3, 44))
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(bivariantInferences.ts, 3, 18))
}
declare const a: (string | number)[] | null[] | undefined[] | {}[];
>a : Symbol(a, Decl(bivariantInferences.ts, 6, 13))
declare const b: (string | number)[] | null[] | undefined[] | {}[];
>b : Symbol(b, Decl(bivariantInferences.ts, 7, 13))
let x = a.equalsShallow(b);
>x : Symbol(x, Decl(bivariantInferences.ts, 9, 3))
>a.equalsShallow : Symbol(equalsShallow, Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20))
>a : Symbol(a, Decl(bivariantInferences.ts, 6, 13))
>equalsShallow : Symbol(equalsShallow, Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20))
>b : Symbol(b, Decl(bivariantInferences.ts, 7, 13))

View file

@ -0,0 +1,26 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts ===
// Repro from #27337
interface Array<T> {
equalsShallow<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>): boolean;
>equalsShallow : <T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean
>this : ReadonlyArray<T>
>other : ReadonlyArray<T>
}
declare const a: (string | number)[] | null[] | undefined[] | {}[];
>a : (string | number)[] | null[] | undefined[] | {}[]
>null : null
declare const b: (string | number)[] | null[] | undefined[] | {}[];
>b : (string | number)[] | null[] | undefined[] | {}[]
>null : null
let x = a.equalsShallow(b);
>x : boolean
>a.equalsShallow(b) : boolean
>a.equalsShallow : (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean) | (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean) | (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean) | (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean)
>a : (string | number)[] | null[] | undefined[] | {}[]
>equalsShallow : (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean) | (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean) | (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean) | (<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>) => boolean)
>b : (string | number)[] | null[] | undefined[] | {}[]

View file

@ -0,0 +1,12 @@
// @strict: true
// Repro from #27337
interface Array<T> {
equalsShallow<T>(this: ReadonlyArray<T>, other: ReadonlyArray<T>): boolean;
}
declare const a: (string | number)[] | null[] | undefined[] | {}[];
declare const b: (string | number)[] | null[] | undefined[] | {}[];
let x = a.equalsShallow(b);