Skip to content

Commit cf2f339

Browse files
Merge pull request #26895 from Microsoft/callableErrors
Find first callable/constructable type in union when appropriate
2 parents d31973b + d067376 commit cf2f339

8 files changed

+190
-11
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11441,7 +11441,8 @@ namespace ts {
1144111441
const bestMatchingType =
1144211442
findMatchingDiscriminantType(source, target) ||
1144311443
findMatchingTypeReferenceOrTypeAliasReference(source, target) ||
11444-
findBestTypeForObjectLiteral(source, target);
11444+
findBestTypeForObjectLiteral(source, target) ||
11445+
findBestTypeForInvokable(source, target);
1144511446

1144611447
isRelatedTo(source, bestMatchingType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true);
1144711448
}
@@ -11472,6 +11473,15 @@ namespace ts {
1147211473
}
1147311474
}
1147411475

11476+
function findBestTypeForInvokable(source: Type, unionTarget: UnionOrIntersectionType) {
11477+
let signatureKind = SignatureKind.Call;
11478+
const hasSignatures = getSignaturesOfType(source, signatureKind).length > 0 ||
11479+
(signatureKind = SignatureKind.Construct, getSignaturesOfType(source, signatureKind).length > 0);
11480+
if (hasSignatures) {
11481+
return find(unionTarget.types, t => getSignaturesOfType(t, signatureKind).length > 0);
11482+
}
11483+
}
11484+
1147511485
// Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly
1147611486
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
1147711487
let match: Type | undefined;

tests/baselines/reference/contextualTypeWithUnionTypeObjectLiteral.errors.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
2222
Type 'string | number' is not assignable to type 'number'.
2323
Type 'string' is not assignable to type 'number'.
2424
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(58,5): error TS2322: Type '(a: string, b: number) => string | number' is not assignable to type '((a: string, b: number) => string) | ((a: string, b: number) => number)'.
25-
Type '(a: string, b: number) => string | number' is not assignable to type '(a: string, b: number) => number'.
26-
Type 'string | number' is not assignable to type 'number'.
27-
Type 'string' is not assignable to type 'number'.
25+
Type '(a: string, b: number) => string | number' is not assignable to type '(a: string, b: number) => string'.
26+
Type 'string | number' is not assignable to type 'string'.
27+
Type 'number' is not assignable to type 'string'.
2828

2929

3030
==== tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts (6 errors) ====
@@ -116,8 +116,8 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
116116
commonMethodDifferentReturnType: (a, b) => strOrNumber,
117117
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118118
!!! error TS2322: Type '(a: string, b: number) => string | number' is not assignable to type '((a: string, b: number) => string) | ((a: string, b: number) => number)'.
119-
!!! error TS2322: Type '(a: string, b: number) => string | number' is not assignable to type '(a: string, b: number) => number'.
120-
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
121-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
119+
!!! error TS2322: Type '(a: string, b: number) => string | number' is not assignable to type '(a: string, b: number) => string'.
120+
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
121+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
122122
!!! related TS6500 tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts:35:5: The expected type comes from property 'commonMethodDifferentReturnType' which is declared here on type 'I11 | I21'
123123
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
tests/cases/compiler/errorsWithInvokablesInUnions01.ts(14,12): error TS2322: Type '(x: string) => void' is not assignable to type 'ConstructableA | IDirectiveLinkFn<number> | IDirectivePrePost<number>'.
2+
Type '(x: string) => void' is not assignable to type 'IDirectiveLinkFn<number>'.
3+
Types of parameters 'x' and 'scope' are incompatible.
4+
Type 'number' is not assignable to type 'string'.
5+
tests/cases/compiler/errorsWithInvokablesInUnions01.ts(16,12): error TS2322: Type 'typeof ctor' is not assignable to type 'ConstructableA | IDirectiveLinkFn<number> | IDirectivePrePost<number>'.
6+
Type 'typeof ctor' is not assignable to type 'ConstructableA'.
7+
Type 'ctor' is not assignable to type '{ somePropA: any; }'.
8+
Property 'somePropA' is missing in type 'ctor'.
9+
10+
11+
==== tests/cases/compiler/errorsWithInvokablesInUnions01.ts (2 errors) ====
12+
interface ConstructableA {
13+
new(): { somePropA: any };
14+
}
15+
16+
interface IDirectiveLinkFn<TScope> {
17+
(scope: TScope): void;
18+
}
19+
20+
interface IDirectivePrePost<TScope> {
21+
pre?: IDirectiveLinkFn<TScope>;
22+
post?: IDirectiveLinkFn<TScope>;
23+
}
24+
25+
export let blah: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = (x: string) => {}
26+
~~~~
27+
!!! error TS2322: Type '(x: string) => void' is not assignable to type 'ConstructableA | IDirectiveLinkFn<number> | IDirectivePrePost<number>'.
28+
!!! error TS2322: Type '(x: string) => void' is not assignable to type 'IDirectiveLinkFn<number>'.
29+
!!! error TS2322: Types of parameters 'x' and 'scope' are incompatible.
30+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
31+
32+
export let ctor: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = class {
33+
~~~~
34+
!!! error TS2322: Type 'typeof ctor' is not assignable to type 'ConstructableA | IDirectiveLinkFn<number> | IDirectivePrePost<number>'.
35+
!!! error TS2322: Type 'typeof ctor' is not assignable to type 'ConstructableA'.
36+
!!! error TS2322: Type 'ctor' is not assignable to type '{ somePropA: any; }'.
37+
!!! error TS2322: Property 'somePropA' is missing in type 'ctor'.
38+
someUnaccountedProp: any;
39+
}
40+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [errorsWithInvokablesInUnions01.ts]
2+
interface ConstructableA {
3+
new(): { somePropA: any };
4+
}
5+
6+
interface IDirectiveLinkFn<TScope> {
7+
(scope: TScope): void;
8+
}
9+
10+
interface IDirectivePrePost<TScope> {
11+
pre?: IDirectiveLinkFn<TScope>;
12+
post?: IDirectiveLinkFn<TScope>;
13+
}
14+
15+
export let blah: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = (x: string) => {}
16+
17+
export let ctor: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = class {
18+
someUnaccountedProp: any;
19+
}
20+
21+
22+
//// [errorsWithInvokablesInUnions01.js]
23+
"use strict";
24+
exports.__esModule = true;
25+
exports.blah = function (x) { };
26+
exports.ctor = /** @class */ (function () {
27+
function class_1() {
28+
}
29+
return class_1;
30+
}());
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
=== tests/cases/compiler/errorsWithInvokablesInUnions01.ts ===
2+
interface ConstructableA {
3+
>ConstructableA : Symbol(ConstructableA, Decl(errorsWithInvokablesInUnions01.ts, 0, 0))
4+
5+
new(): { somePropA: any };
6+
>somePropA : Symbol(somePropA, Decl(errorsWithInvokablesInUnions01.ts, 1, 10))
7+
}
8+
9+
interface IDirectiveLinkFn<TScope> {
10+
>IDirectiveLinkFn : Symbol(IDirectiveLinkFn, Decl(errorsWithInvokablesInUnions01.ts, 2, 1))
11+
>TScope : Symbol(TScope, Decl(errorsWithInvokablesInUnions01.ts, 4, 27))
12+
13+
(scope: TScope): void;
14+
>scope : Symbol(scope, Decl(errorsWithInvokablesInUnions01.ts, 5, 5))
15+
>TScope : Symbol(TScope, Decl(errorsWithInvokablesInUnions01.ts, 4, 27))
16+
}
17+
18+
interface IDirectivePrePost<TScope> {
19+
>IDirectivePrePost : Symbol(IDirectivePrePost, Decl(errorsWithInvokablesInUnions01.ts, 6, 1))
20+
>TScope : Symbol(TScope, Decl(errorsWithInvokablesInUnions01.ts, 8, 28))
21+
22+
pre?: IDirectiveLinkFn<TScope>;
23+
>pre : Symbol(IDirectivePrePost.pre, Decl(errorsWithInvokablesInUnions01.ts, 8, 37))
24+
>IDirectiveLinkFn : Symbol(IDirectiveLinkFn, Decl(errorsWithInvokablesInUnions01.ts, 2, 1))
25+
>TScope : Symbol(TScope, Decl(errorsWithInvokablesInUnions01.ts, 8, 28))
26+
27+
post?: IDirectiveLinkFn<TScope>;
28+
>post : Symbol(IDirectivePrePost.post, Decl(errorsWithInvokablesInUnions01.ts, 9, 35))
29+
>IDirectiveLinkFn : Symbol(IDirectiveLinkFn, Decl(errorsWithInvokablesInUnions01.ts, 2, 1))
30+
>TScope : Symbol(TScope, Decl(errorsWithInvokablesInUnions01.ts, 8, 28))
31+
}
32+
33+
export let blah: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = (x: string) => {}
34+
>blah : Symbol(blah, Decl(errorsWithInvokablesInUnions01.ts, 13, 10))
35+
>IDirectiveLinkFn : Symbol(IDirectiveLinkFn, Decl(errorsWithInvokablesInUnions01.ts, 2, 1))
36+
>ConstructableA : Symbol(ConstructableA, Decl(errorsWithInvokablesInUnions01.ts, 0, 0))
37+
>IDirectivePrePost : Symbol(IDirectivePrePost, Decl(errorsWithInvokablesInUnions01.ts, 6, 1))
38+
>x : Symbol(x, Decl(errorsWithInvokablesInUnions01.ts, 13, 90))
39+
40+
export let ctor: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = class {
41+
>ctor : Symbol(ctor, Decl(errorsWithInvokablesInUnions01.ts, 15, 10))
42+
>IDirectiveLinkFn : Symbol(IDirectiveLinkFn, Decl(errorsWithInvokablesInUnions01.ts, 2, 1))
43+
>ConstructableA : Symbol(ConstructableA, Decl(errorsWithInvokablesInUnions01.ts, 0, 0))
44+
>IDirectivePrePost : Symbol(IDirectivePrePost, Decl(errorsWithInvokablesInUnions01.ts, 6, 1))
45+
46+
someUnaccountedProp: any;
47+
>someUnaccountedProp : Symbol(ctor.someUnaccountedProp, Decl(errorsWithInvokablesInUnions01.ts, 15, 96))
48+
}
49+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/compiler/errorsWithInvokablesInUnions01.ts ===
2+
interface ConstructableA {
3+
new(): { somePropA: any };
4+
>somePropA : any
5+
}
6+
7+
interface IDirectiveLinkFn<TScope> {
8+
(scope: TScope): void;
9+
>scope : TScope
10+
}
11+
12+
interface IDirectivePrePost<TScope> {
13+
pre?: IDirectiveLinkFn<TScope>;
14+
>pre : IDirectiveLinkFn<TScope>
15+
16+
post?: IDirectiveLinkFn<TScope>;
17+
>post : IDirectiveLinkFn<TScope>
18+
}
19+
20+
export let blah: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = (x: string) => {}
21+
>blah : ConstructableA | IDirectiveLinkFn<number> | IDirectivePrePost<number>
22+
>(x: string) => {} : (x: string) => void
23+
>x : string
24+
25+
export let ctor: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = class {
26+
>ctor : ConstructableA | IDirectiveLinkFn<number> | IDirectivePrePost<number>
27+
>class { someUnaccountedProp: any;} : typeof ctor
28+
29+
someUnaccountedProp: any;
30+
>someUnaccountedProp : any
31+
}
32+

tests/baselines/reference/functionExpressionContextualTyping2.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
tests/cases/conformance/expressions/contextualTyping/functionExpressionContextualTyping2.ts(11,1): error TS2322: Type '(foo: number, bar: string) => boolean' is not assignable to type '((n: number, s: string) => number) | ((n: number, s: string) => string)'.
2-
Type '(foo: number, bar: string) => boolean' is not assignable to type '(n: number, s: string) => string'.
3-
Type 'boolean' is not assignable to type 'string'.
2+
Type '(foo: number, bar: string) => boolean' is not assignable to type '(n: number, s: string) => number'.
3+
Type 'boolean' is not assignable to type 'number'.
44

55

66
==== tests/cases/conformance/expressions/contextualTyping/functionExpressionContextualTyping2.ts (1 errors) ====
@@ -17,5 +17,5 @@ tests/cases/conformance/expressions/contextualTyping/functionExpressionContextua
1717
a1 = (foo, bar) => { return true; } // Error
1818
~~
1919
!!! error TS2322: Type '(foo: number, bar: string) => boolean' is not assignable to type '((n: number, s: string) => number) | ((n: number, s: string) => string)'.
20-
!!! error TS2322: Type '(foo: number, bar: string) => boolean' is not assignable to type '(n: number, s: string) => string'.
21-
!!! error TS2322: Type 'boolean' is not assignable to type 'string'.
20+
!!! error TS2322: Type '(foo: number, bar: string) => boolean' is not assignable to type '(n: number, s: string) => number'.
21+
!!! error TS2322: Type 'boolean' is not assignable to type 'number'.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
interface ConstructableA {
2+
new(): { somePropA: any };
3+
}
4+
5+
interface IDirectiveLinkFn<TScope> {
6+
(scope: TScope): void;
7+
}
8+
9+
interface IDirectivePrePost<TScope> {
10+
pre?: IDirectiveLinkFn<TScope>;
11+
post?: IDirectiveLinkFn<TScope>;
12+
}
13+
14+
export let blah: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = (x: string) => {}
15+
16+
export let ctor: IDirectiveLinkFn<number> | ConstructableA | IDirectivePrePost<number> = class {
17+
someUnaccountedProp: any;
18+
}

0 commit comments

Comments
 (0)