Skip to content

Commit 19c72c7

Browse files
authored
Allow untyped calls on unions of untyped things (#29265)
1 parent 4a0bc59 commit 19c72c7

File tree

5 files changed

+122
-3
lines changed

5 files changed

+122
-3
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6930,7 +6930,7 @@ namespace ts {
69306930
function resolveUnionTypeMembers(type: UnionType) {
69316931
// The members and properties collections are empty for union types. To get all properties of a union
69326932
// type use getPropertiesOfType (only the language service uses this).
6933-
const callSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Call)));
6933+
const callSignatures = getUnionSignatures(map(type.types, t => t === globalFunctionType ? [unknownSignature] : getSignaturesOfType(t, SignatureKind.Call)));
69346934
const constructSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Construct)));
69356935
const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String);
69366936
const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number);
@@ -20506,9 +20506,9 @@ namespace ts {
2050620506
* If FuncExpr is of type Any, or of an object type that has no call or construct signatures
2050720507
* but is a subtype of the Function interface, the call is an untyped function call.
2050820508
*/
20509-
function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) {
20509+
function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number): boolean {
2051020510
// We exclude union types because we may have a union of function types that happen to have no common signatures.
20511-
return isTypeAny(funcType) || isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter ||
20511+
return isTypeAny(funcType) || isTypeAny(apparentFuncType) && !!(funcType.flags & TypeFlags.TypeParameter) ||
2051220512
!numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType);
2051320513
}
2051420514

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [unionOfFunctionAndSignatureIsCallable.ts]
2+
function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) {
3+
const a = c1();
4+
const b = c2();
5+
const c = callable();
6+
}
7+
8+
function f2(fetcherParams: object | (() => object)) {
9+
const data = typeof fetcherParams === 'function'
10+
? fetcherParams()
11+
: fetcherParams
12+
}
13+
14+
15+
//// [unionOfFunctionAndSignatureIsCallable.js]
16+
function f1(c1, c2, callable) {
17+
var a = c1();
18+
var b = c2();
19+
var c = callable();
20+
}
21+
function f2(fetcherParams) {
22+
var data = typeof fetcherParams === 'function'
23+
? fetcherParams()
24+
: fetcherParams;
25+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts ===
2+
function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) {
3+
>f1 : Symbol(f1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 0))
4+
>c1 : Symbol(c1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 12))
5+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
6+
>c2 : Symbol(c2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 25))
7+
>callable : Symbol(callable, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 43))
8+
>c1 : Symbol(c1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 12))
9+
>c2 : Symbol(c2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 25))
10+
11+
const a = c1();
12+
>a : Symbol(a, Decl(unionOfFunctionAndSignatureIsCallable.ts, 1, 9))
13+
>c1 : Symbol(c1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 12))
14+
15+
const b = c2();
16+
>b : Symbol(b, Decl(unionOfFunctionAndSignatureIsCallable.ts, 2, 9))
17+
>c2 : Symbol(c2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 25))
18+
19+
const c = callable();
20+
>c : Symbol(c, Decl(unionOfFunctionAndSignatureIsCallable.ts, 3, 9))
21+
>callable : Symbol(callable, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 43))
22+
}
23+
24+
function f2(fetcherParams: object | (() => object)) {
25+
>f2 : Symbol(f2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 4, 1))
26+
>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12))
27+
28+
const data = typeof fetcherParams === 'function'
29+
>data : Symbol(data, Decl(unionOfFunctionAndSignatureIsCallable.ts, 7, 9))
30+
>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12))
31+
32+
? fetcherParams()
33+
>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12))
34+
35+
: fetcherParams
36+
>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12))
37+
}
38+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts ===
2+
function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) {
3+
>f1 : (c1: Function, c2: () => object, callable: Function | (() => object)) => void
4+
>c1 : Function
5+
>c2 : () => object
6+
>callable : Function | (() => object)
7+
>c1 : Function
8+
>c2 : () => object
9+
10+
const a = c1();
11+
>a : any
12+
>c1() : any
13+
>c1 : Function
14+
15+
const b = c2();
16+
>b : object
17+
>c2() : object
18+
>c2 : () => object
19+
20+
const c = callable();
21+
>c : any
22+
>callable() : any
23+
>callable : Function | (() => object)
24+
}
25+
26+
function f2(fetcherParams: object | (() => object)) {
27+
>f2 : (fetcherParams: object | (() => object)) => void
28+
>fetcherParams : object | (() => object)
29+
30+
const data = typeof fetcherParams === 'function'
31+
>data : any
32+
>typeof fetcherParams === 'function' ? fetcherParams() : fetcherParams : any
33+
>typeof fetcherParams === 'function' : boolean
34+
>typeof fetcherParams : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
35+
>fetcherParams : object | (() => object)
36+
>'function' : "function"
37+
38+
? fetcherParams()
39+
>fetcherParams() : any
40+
>fetcherParams : Function | (() => object)
41+
42+
: fetcherParams
43+
>fetcherParams : object
44+
}
45+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) {
2+
const a = c1();
3+
const b = c2();
4+
const c = callable();
5+
}
6+
7+
function f2(fetcherParams: object | (() => object)) {
8+
const data = typeof fetcherParams === 'function'
9+
? fetcherParams()
10+
: fetcherParams
11+
}

0 commit comments

Comments
 (0)