From 7ee3464234d2a3ca010ee11e00feade8d592c4c7 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 4 Jan 2019 14:06:13 -0800 Subject: [PATCH] Allow untyped calls on unions of untyped things --- src/compiler/checker.ts | 6 +-- .../unionOfFunctionAndSignatureIsCallable.js | 25 +++++++++++ ...onOfFunctionAndSignatureIsCallable.symbols | 38 ++++++++++++++++ ...nionOfFunctionAndSignatureIsCallable.types | 45 +++++++++++++++++++ .../unionOfFunctionAndSignatureIsCallable.ts | 11 +++++ 5 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.js create mode 100644 tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.symbols create mode 100644 tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.types create mode 100644 tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index de44e8bb37ca7..86822e2487846 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6930,7 +6930,7 @@ namespace ts { function resolveUnionTypeMembers(type: UnionType) { // The members and properties collections are empty for union types. To get all properties of a union // type use getPropertiesOfType (only the language service uses this). - const callSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Call))); + const callSignatures = getUnionSignatures(map(type.types, t => t === globalFunctionType ? [unknownSignature] : getSignaturesOfType(t, SignatureKind.Call))); const constructSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Construct))); const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String); const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number); @@ -20506,9 +20506,9 @@ namespace ts { * If FuncExpr is of type Any, or of an object type that has no call or construct signatures * but is a subtype of the Function interface, the call is an untyped function call. */ - function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) { + function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number): boolean { // We exclude union types because we may have a union of function types that happen to have no common signatures. - return isTypeAny(funcType) || isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter || + return isTypeAny(funcType) || isTypeAny(apparentFuncType) && !!(funcType.flags & TypeFlags.TypeParameter) || !numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType); } diff --git a/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.js b/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.js new file mode 100644 index 0000000000000..eb3e073eec6e1 --- /dev/null +++ b/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.js @@ -0,0 +1,25 @@ +//// [unionOfFunctionAndSignatureIsCallable.ts] +function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) { + const a = c1(); + const b = c2(); + const c = callable(); +} + +function f2(fetcherParams: object | (() => object)) { + const data = typeof fetcherParams === 'function' + ? fetcherParams() + : fetcherParams +} + + +//// [unionOfFunctionAndSignatureIsCallable.js] +function f1(c1, c2, callable) { + var a = c1(); + var b = c2(); + var c = callable(); +} +function f2(fetcherParams) { + var data = typeof fetcherParams === 'function' + ? fetcherParams() + : fetcherParams; +} diff --git a/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.symbols b/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.symbols new file mode 100644 index 0000000000000..0ab573e429716 --- /dev/null +++ b/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.symbols @@ -0,0 +1,38 @@ +=== tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts === +function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) { +>f1 : Symbol(f1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 0)) +>c1 : Symbol(c1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 12)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>c2 : Symbol(c2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 25)) +>callable : Symbol(callable, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 43)) +>c1 : Symbol(c1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 12)) +>c2 : Symbol(c2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 25)) + + const a = c1(); +>a : Symbol(a, Decl(unionOfFunctionAndSignatureIsCallable.ts, 1, 9)) +>c1 : Symbol(c1, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 12)) + + const b = c2(); +>b : Symbol(b, Decl(unionOfFunctionAndSignatureIsCallable.ts, 2, 9)) +>c2 : Symbol(c2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 25)) + + const c = callable(); +>c : Symbol(c, Decl(unionOfFunctionAndSignatureIsCallable.ts, 3, 9)) +>callable : Symbol(callable, Decl(unionOfFunctionAndSignatureIsCallable.ts, 0, 43)) +} + +function f2(fetcherParams: object | (() => object)) { +>f2 : Symbol(f2, Decl(unionOfFunctionAndSignatureIsCallable.ts, 4, 1)) +>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12)) + + const data = typeof fetcherParams === 'function' +>data : Symbol(data, Decl(unionOfFunctionAndSignatureIsCallable.ts, 7, 9)) +>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12)) + + ? fetcherParams() +>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12)) + + : fetcherParams +>fetcherParams : Symbol(fetcherParams, Decl(unionOfFunctionAndSignatureIsCallable.ts, 6, 12)) +} + diff --git a/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.types b/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.types new file mode 100644 index 0000000000000..96e49d80d5075 --- /dev/null +++ b/tests/baselines/reference/unionOfFunctionAndSignatureIsCallable.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts === +function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) { +>f1 : (c1: Function, c2: () => object, callable: Function | (() => object)) => void +>c1 : Function +>c2 : () => object +>callable : Function | (() => object) +>c1 : Function +>c2 : () => object + + const a = c1(); +>a : any +>c1() : any +>c1 : Function + + const b = c2(); +>b : object +>c2() : object +>c2 : () => object + + const c = callable(); +>c : any +>callable() : any +>callable : Function | (() => object) +} + +function f2(fetcherParams: object | (() => object)) { +>f2 : (fetcherParams: object | (() => object)) => void +>fetcherParams : object | (() => object) + + const data = typeof fetcherParams === 'function' +>data : any +>typeof fetcherParams === 'function' ? fetcherParams() : fetcherParams : any +>typeof fetcherParams === 'function' : boolean +>typeof fetcherParams : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>fetcherParams : object | (() => object) +>'function' : "function" + + ? fetcherParams() +>fetcherParams() : any +>fetcherParams : Function | (() => object) + + : fetcherParams +>fetcherParams : object +} + diff --git a/tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts b/tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts new file mode 100644 index 0000000000000..8555ed715cdff --- /dev/null +++ b/tests/cases/compiler/unionOfFunctionAndSignatureIsCallable.ts @@ -0,0 +1,11 @@ +function f1(c1: Function, c2: () => object, callable: typeof c1 | typeof c2) { + const a = c1(); + const b = c2(); + const c = callable(); +} + +function f2(fetcherParams: object | (() => object)) { + const data = typeof fetcherParams === 'function' + ? fetcherParams() + : fetcherParams +}