Merge pull request #19107 from Microsoft/fixRecursiveCallbacks

Fix checking of recursive callback types
This commit is contained in:
Anders Hejlsberg 2017-10-12 00:28:16 +01:00 committed by GitHub
commit 6cf41ae882
6 changed files with 117 additions and 3 deletions

View File

@ -8610,16 +8610,16 @@ namespace ts {
for (let i = 0; i < checkCount; i++) {
const sourceType = i < sourceMax ? getTypeOfParameter(sourceParams[i]) : getRestTypeOfSignature(source);
const targetType = i < targetMax ? getTypeOfParameter(targetParams[i]) : getRestTypeOfSignature(target);
const sourceSig = getSingleCallSignature(getNonNullableType(sourceType));
const targetSig = getSingleCallSignature(getNonNullableType(targetType));
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
// they naturally relate only contra-variantly). However, if the source and target parameters both have
// function types with a single call signature, we known we are relating two callback parameters. In
// function types with a single call signature, we know we are relating two callback parameters. In
// that case it is sufficient to only relate the parameters of the signatures co-variantly because,
// similar to return values, callback parameters are output positions. This means that a Promise<T>,
// where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
// with respect to T.
const sourceSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
const targetSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(targetType));
const callbacks = sourceSig && targetSig && !sourceSig.typePredicate && !targetSig.typePredicate &&
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
const related = callbacks ?

View File

@ -0,0 +1,24 @@
tests/cases/compiler/mutuallyRecursiveCallbacks.ts(7,1): error TS2322: Type '<T>(bar: Bar<T>) => void' is not assignable to type 'Bar<{}>'.
Types of parameters 'bar' and 'foo' are incompatible.
Types of parameters 'bar' and 'foo' are incompatible.
Type 'Foo<{}>' is not assignable to type 'Bar<{}>'.
Types of parameters 'bar' and 'foo' are incompatible.
Type 'void' is not assignable to type 'Foo<{}>'.
==== tests/cases/compiler/mutuallyRecursiveCallbacks.ts (1 errors) ====
// Repro from #18277
interface Foo<T> { (bar: Bar<T>): void };
type Bar<T> = (foo: Foo<T>) => Foo<T>;
declare function foo<T>(bar: Bar<T>): void;
declare var bar: Bar<{}>;
bar = foo;
~~~
!!! error TS2322: Type '<T>(bar: Bar<T>) => void' is not assignable to type 'Bar<{}>'.
!!! error TS2322: Types of parameters 'bar' and 'foo' are incompatible.
!!! error TS2322: Types of parameters 'bar' and 'foo' are incompatible.
!!! error TS2322: Type 'Foo<{}>' is not assignable to type 'Bar<{}>'.
!!! error TS2322: Types of parameters 'bar' and 'foo' are incompatible.
!!! error TS2322: Type 'void' is not assignable to type 'Foo<{}>'.

View File

@ -0,0 +1,14 @@
//// [mutuallyRecursiveCallbacks.ts]
// Repro from #18277
interface Foo<T> { (bar: Bar<T>): void };
type Bar<T> = (foo: Foo<T>) => Foo<T>;
declare function foo<T>(bar: Bar<T>): void;
declare var bar: Bar<{}>;
bar = foo;
//// [mutuallyRecursiveCallbacks.js]
// Repro from #18277
;
bar = foo;

View File

@ -0,0 +1,34 @@
=== tests/cases/compiler/mutuallyRecursiveCallbacks.ts ===
// Repro from #18277
interface Foo<T> { (bar: Bar<T>): void };
>Foo : Symbol(Foo, Decl(mutuallyRecursiveCallbacks.ts, 0, 0))
>T : Symbol(T, Decl(mutuallyRecursiveCallbacks.ts, 2, 14))
>bar : Symbol(bar, Decl(mutuallyRecursiveCallbacks.ts, 2, 20))
>Bar : Symbol(Bar, Decl(mutuallyRecursiveCallbacks.ts, 2, 41))
>T : Symbol(T, Decl(mutuallyRecursiveCallbacks.ts, 2, 14))
type Bar<T> = (foo: Foo<T>) => Foo<T>;
>Bar : Symbol(Bar, Decl(mutuallyRecursiveCallbacks.ts, 2, 41))
>T : Symbol(T, Decl(mutuallyRecursiveCallbacks.ts, 3, 9))
>foo : Symbol(foo, Decl(mutuallyRecursiveCallbacks.ts, 3, 15))
>Foo : Symbol(Foo, Decl(mutuallyRecursiveCallbacks.ts, 0, 0))
>T : Symbol(T, Decl(mutuallyRecursiveCallbacks.ts, 3, 9))
>Foo : Symbol(Foo, Decl(mutuallyRecursiveCallbacks.ts, 0, 0))
>T : Symbol(T, Decl(mutuallyRecursiveCallbacks.ts, 3, 9))
declare function foo<T>(bar: Bar<T>): void;
>foo : Symbol(foo, Decl(mutuallyRecursiveCallbacks.ts, 3, 38))
>T : Symbol(T, Decl(mutuallyRecursiveCallbacks.ts, 4, 21))
>bar : Symbol(bar, Decl(mutuallyRecursiveCallbacks.ts, 4, 24))
>Bar : Symbol(Bar, Decl(mutuallyRecursiveCallbacks.ts, 2, 41))
>T : Symbol(T, Decl(mutuallyRecursiveCallbacks.ts, 4, 21))
declare var bar: Bar<{}>;
>bar : Symbol(bar, Decl(mutuallyRecursiveCallbacks.ts, 5, 11))
>Bar : Symbol(Bar, Decl(mutuallyRecursiveCallbacks.ts, 2, 41))
bar = foo;
>bar : Symbol(bar, Decl(mutuallyRecursiveCallbacks.ts, 5, 11))
>foo : Symbol(foo, Decl(mutuallyRecursiveCallbacks.ts, 3, 38))

View File

@ -0,0 +1,35 @@
=== tests/cases/compiler/mutuallyRecursiveCallbacks.ts ===
// Repro from #18277
interface Foo<T> { (bar: Bar<T>): void };
>Foo : Foo<T>
>T : T
>bar : Bar<T>
>Bar : Bar<T>
>T : T
type Bar<T> = (foo: Foo<T>) => Foo<T>;
>Bar : Bar<T>
>T : T
>foo : Foo<T>
>Foo : Foo<T>
>T : T
>Foo : Foo<T>
>T : T
declare function foo<T>(bar: Bar<T>): void;
>foo : <T>(bar: Bar<T>) => void
>T : T
>bar : Bar<T>
>Bar : Bar<T>
>T : T
declare var bar: Bar<{}>;
>bar : Bar<{}>
>Bar : Bar<T>
bar = foo;
>bar = foo : <T>(bar: Bar<T>) => void
>bar : Bar<{}>
>foo : <T>(bar: Bar<T>) => void

View File

@ -0,0 +1,7 @@
// Repro from #18277
interface Foo<T> { (bar: Bar<T>): void };
type Bar<T> = (foo: Foo<T>) => Foo<T>;
declare function foo<T>(bar: Bar<T>): void;
declare var bar: Bar<{}>;
bar = foo;