From bf5b762bf63d8d4ba4d3d7c0da240707079db16f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 21 Mar 2019 21:22:43 +1100 Subject: [PATCH 1/4] Merge overloads that only differ by return type when intersecting. --- src/compiler/checker.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ea44fb0f3bd74..1721bc0cac2df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7087,13 +7087,49 @@ namespace ts { } constructSignatures = concatenate(constructSignatures, signatures); } + callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call)); stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); } + + callSignatures = intersectCallSignatures(callSignatures); + setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } + function intersectCallSignatures(callSignatures: ReadonlyArray): ReadonlyArray { + // Overloads that differ only be return type make no sense. So: + // 1. Group all the call signatures by their parameter types. Each group is an overload. + // 2. Create a new signature for each group where the return type is the intersection + // of the return types of the signatures in that group. + const groups: Signature[][] = []; + + callSignatures.forEach((callSignature) => { + const matchingGroup = find(groups, (group) => { + const candidate = group[0]; + const candidateParameters = candidate.parameters; + const thisCallParameters = callSignature.parameters; + return candidateParameters.length === thisCallParameters.length && + every(thisCallParameters, (parameter, index) => isTypeIdenticalTo(getTypeOfParameter(parameter), getTypeOfParameter(candidateParameters[index]))); + }); + if (matchingGroup === undefined) { + groups.push([callSignature]); + } else { + matchingGroup.push(callSignature); + } + }); + + return groups.map((signatures) => { + if (signatures.length === 1) { + return signatures[0]; + } + const clone = cloneSignature(signatures[0]); + clone.resolvedReturnType = getIntersectionType(signatures.map(signature => getReturnTypeOfSignature(signature))); + return clone; + }); + } + /** * Converts an AnonymousType to a ResolvedType. */ From ca33f40dbed1978cb82836d242cf30ffd6c3a2e0 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 21 Mar 2019 22:28:03 +1100 Subject: [PATCH 2/4] Fix lint, other style conformance. --- src/compiler/checker.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1721bc0cac2df..949ad2aaa7d09 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7087,18 +7087,19 @@ namespace ts { } constructSignatures = concatenate(constructSignatures, signatures); } - callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call)); stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); } - callSignatures = intersectCallSignatures(callSignatures); - setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } function intersectCallSignatures(callSignatures: ReadonlyArray): ReadonlyArray { + if (callSignatures.length === 0) { + return callSignatures; + } + // Overloads that differ only be return type make no sense. So: // 1. Group all the call signatures by their parameter types. Each group is an overload. // 2. Create a new signature for each group where the return type is the intersection @@ -7115,7 +7116,8 @@ namespace ts { }); if (matchingGroup === undefined) { groups.push([callSignature]); - } else { + } + else { matchingGroup.push(callSignature); } }); From 201c263a6ffc602021824bd50e4e0ec72a5672aa Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 21 Mar 2019 23:18:18 +1100 Subject: [PATCH 3/4] Update one test, add another. --- ...xGenericTagHasCorrectInferences.errors.txt | 13 +- .../intersectionOfCallsWithSameParameters.js | 66 ++++++++ ...ersectionOfCallsWithSameParameters.symbols | 131 ++++++++++++++++ ...ntersectionOfCallsWithSameParameters.types | 144 ++++++++++++++++++ .../intersectionOfCallsWithSameParameters.ts | 38 +++++ 5 files changed, 384 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/intersectionOfCallsWithSameParameters.js create mode 100644 tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols create mode 100644 tests/baselines/reference/intersectionOfCallsWithSameParameters.types create mode 100644 tests/cases/compiler/intersectionOfCallsWithSameParameters.ts diff --git a/tests/baselines/reference/checkJsxGenericTagHasCorrectInferences.errors.txt b/tests/baselines/reference/checkJsxGenericTagHasCorrectInferences.errors.txt index 36ebc70b76ce3..d5bd047ffe41e 100644 --- a/tests/baselines/reference/checkJsxGenericTagHasCorrectInferences.errors.txt +++ b/tests/baselines/reference/checkJsxGenericTagHasCorrectInferences.errors.txt @@ -1,6 +1,5 @@ -tests/cases/conformance/jsx/file.tsx(13,54): error TS2322: Type '(a: { x: string; }) => string' is not assignable to type '((a: { x: string; }) => string) & ((cur: { x: string; }) => { x: string; })'. - Type '(a: { x: string; }) => string' is not assignable to type '(cur: { x: string; }) => { x: string; }'. - Type 'string' is not assignable to type '{ x: string; }'. +tests/cases/conformance/jsx/file.tsx(13,71): error TS2322: Type 'string' is not assignable to type 'string & { x: string; }'. + Type 'string' is not assignable to type '{ x: string; }'. ==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== @@ -17,8 +16,6 @@ tests/cases/conformance/jsx/file.tsx(13,54): error TS2322: Type '(a: { x: string let b = a} />; // No error - Values should be reinstantiated with `number` (since `object` is a default, not a constraint) let c = ({ x: a.x })} />; // No Error let d = a.x} />; // Error - `string` is not assignable to `{x: string}` - ~~~~~~~~~~ -!!! error TS2322: Type '(a: { x: string; }) => string' is not assignable to type '((a: { x: string; }) => string) & ((cur: { x: string; }) => { x: string; })'. -!!! error TS2322: Type '(a: { x: string; }) => string' is not assignable to type '(cur: { x: string; }) => { x: string; }'. -!!! error TS2322: Type 'string' is not assignable to type '{ x: string; }'. -!!! related TS6500 tests/cases/conformance/jsx/file.tsx:13:54: The expected type comes from property 'nextValues' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes string; }, { x: string; }>> & { initialValues: { x: string; }; nextValues: (a: { x: string; }) => string; } & BaseProps<{ x: string; }> & { children?: ReactNode; }' \ No newline at end of file + ~~~ +!!! error TS2322: Type 'string' is not assignable to type 'string & { x: string; }'. +!!! error TS2322: Type 'string' is not assignable to type '{ x: string; }'. \ No newline at end of file diff --git a/tests/baselines/reference/intersectionOfCallsWithSameParameters.js b/tests/baselines/reference/intersectionOfCallsWithSameParameters.js new file mode 100644 index 0000000000000..87ce4358342f5 --- /dev/null +++ b/tests/baselines/reference/intersectionOfCallsWithSameParameters.js @@ -0,0 +1,66 @@ +//// [intersectionOfCallsWithSameParameters.ts] +interface One { + overload(id: string): { one: number }; + intersect(id: string): { one: number }; +} + +interface Two { + overload(id: number): { two: number }; + intersect(id: string): { two: number }; +} + +class Both implements One, Two { + overload(id: number): { two: number }; + overload(id: string): { one: number }; + overload(id: string | number): { one: number, two: number } { + return { + one: 1, + two: 2 + }; + } + + intersect(id: string): { one: number, two: number } { + return { + one: 1, + two: 2 + }; + } +} + +const b = new Both(); +const intersect: { one: number, two: number } = b.intersect('test'); +const overloadA: { one: number } = b.overload('test'); +const overloadB: { two: number } = b.overload(4); +const bAs: One & Two = b; +const asIntersect: { one: number, two: number } = bAs.intersect('test'); +const asOverloadA: { one: number } = bAs.overload('test'); +const asOverloadB: { two: number } = bAs.overload(4); + + +//// [intersectionOfCallsWithSameParameters.js] +"use strict"; +var Both = /** @class */ (function () { + function Both() { + } + Both.prototype.overload = function (id) { + return { + one: 1, + two: 2 + }; + }; + Both.prototype.intersect = function (id) { + return { + one: 1, + two: 2 + }; + }; + return Both; +}()); +var b = new Both(); +var intersect = b.intersect('test'); +var overloadA = b.overload('test'); +var overloadB = b.overload(4); +var bAs = b; +var asIntersect = bAs.intersect('test'); +var asOverloadA = bAs.overload('test'); +var asOverloadB = bAs.overload(4); diff --git a/tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols b/tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols new file mode 100644 index 0000000000000..1f9bf9134c28d --- /dev/null +++ b/tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols @@ -0,0 +1,131 @@ +=== tests/cases/compiler/intersectionOfCallsWithSameParameters.ts === +interface One { +>One : Symbol(One, Decl(intersectionOfCallsWithSameParameters.ts, 0, 0)) + + overload(id: string): { one: number }; +>overload : Symbol(One.overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 1, 13)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 1, 27)) + + intersect(id: string): { one: number }; +>intersect : Symbol(One.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 1, 42)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 2, 14)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 2, 28)) +} + +interface Two { +>Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 3, 1)) + + overload(id: number): { two: number }; +>overload : Symbol(Two.overload, Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 6, 13)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 6, 27)) + + intersect(id: string): { two: number }; +>intersect : Symbol(Two.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 6, 42)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 7, 14)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 7, 28)) +} + +class Both implements One, Two { +>Both : Symbol(Both, Decl(intersectionOfCallsWithSameParameters.ts, 8, 1)) +>One : Symbol(One, Decl(intersectionOfCallsWithSameParameters.ts, 0, 0)) +>Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 3, 1)) + + overload(id: number): { two: number }; +>overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 11, 13)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 11, 27)) + + overload(id: string): { one: number }; +>overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 12, 13)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 12, 27)) + + overload(id: string | number): { one: number, two: number } { +>overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 13, 13)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 13, 36)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 13, 49)) + + return { + one: 1, +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 14, 16)) + + two: 2 +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 15, 19)) + + }; + } + + intersect(id: string): { one: number, two: number } { +>intersect : Symbol(Both.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 18, 5)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 20, 14)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 20, 28)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 20, 41)) + + return { + one: 1, +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 21, 16)) + + two: 2 +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 22, 19)) + + }; + } +} + +const b = new Both(); +>b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) +>Both : Symbol(Both, Decl(intersectionOfCallsWithSameParameters.ts, 8, 1)) + +const intersect: { one: number, two: number } = b.intersect('test'); +>intersect : Symbol(intersect, Decl(intersectionOfCallsWithSameParameters.ts, 29, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 29, 18)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 29, 31)) +>b.intersect : Symbol(Both.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 18, 5)) +>b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) +>intersect : Symbol(Both.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 18, 5)) + +const overloadA: { one: number } = b.overload('test'); +>overloadA : Symbol(overloadA, Decl(intersectionOfCallsWithSameParameters.ts, 30, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 30, 18)) +>b.overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) +>b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) +>overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) + +const overloadB: { two: number } = b.overload(4); +>overloadB : Symbol(overloadB, Decl(intersectionOfCallsWithSameParameters.ts, 31, 5)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 31, 18)) +>b.overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) +>b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) +>overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) + +const bAs: One & Two = b; +>bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) +>One : Symbol(One, Decl(intersectionOfCallsWithSameParameters.ts, 0, 0)) +>Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 3, 1)) +>b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) + +const asIntersect: { one: number, two: number } = bAs.intersect('test'); +>asIntersect : Symbol(asIntersect, Decl(intersectionOfCallsWithSameParameters.ts, 33, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 33, 20)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 33, 33)) +>bAs.intersect : Symbol(intersect, Decl(intersectionOfCallsWithSameParameters.ts, 1, 42), Decl(intersectionOfCallsWithSameParameters.ts, 6, 42)) +>bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) +>intersect : Symbol(intersect, Decl(intersectionOfCallsWithSameParameters.ts, 1, 42), Decl(intersectionOfCallsWithSameParameters.ts, 6, 42)) + +const asOverloadA: { one: number } = bAs.overload('test'); +>asOverloadA : Symbol(asOverloadA, Decl(intersectionOfCallsWithSameParameters.ts, 34, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 34, 20)) +>bAs.overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) +>bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) +>overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) + +const asOverloadB: { two: number } = bAs.overload(4); +>asOverloadB : Symbol(asOverloadB, Decl(intersectionOfCallsWithSameParameters.ts, 35, 5)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 35, 20)) +>bAs.overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) +>bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) +>overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) + diff --git a/tests/baselines/reference/intersectionOfCallsWithSameParameters.types b/tests/baselines/reference/intersectionOfCallsWithSameParameters.types new file mode 100644 index 0000000000000..96238b472bb23 --- /dev/null +++ b/tests/baselines/reference/intersectionOfCallsWithSameParameters.types @@ -0,0 +1,144 @@ +=== tests/cases/compiler/intersectionOfCallsWithSameParameters.ts === +interface One { + overload(id: string): { one: number }; +>overload : (id: string) => { one: number; } +>id : string +>one : number + + intersect(id: string): { one: number }; +>intersect : (id: string) => { one: number; } +>id : string +>one : number +} + +interface Two { + overload(id: number): { two: number }; +>overload : (id: number) => { two: number; } +>id : number +>two : number + + intersect(id: string): { two: number }; +>intersect : (id: string) => { two: number; } +>id : string +>two : number +} + +class Both implements One, Two { +>Both : Both + + overload(id: number): { two: number }; +>overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>id : number +>two : number + + overload(id: string): { one: number }; +>overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>id : string +>one : number + + overload(id: string | number): { one: number, two: number } { +>overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>id : string | number +>one : number +>two : number + + return { +>{ one: 1, two: 2 } : { one: number; two: number; } + + one: 1, +>one : number +>1 : 1 + + two: 2 +>two : number +>2 : 2 + + }; + } + + intersect(id: string): { one: number, two: number } { +>intersect : (id: string) => { one: number; two: number; } +>id : string +>one : number +>two : number + + return { +>{ one: 1, two: 2 } : { one: number; two: number; } + + one: 1, +>one : number +>1 : 1 + + two: 2 +>two : number +>2 : 2 + + }; + } +} + +const b = new Both(); +>b : Both +>new Both() : Both +>Both : typeof Both + +const intersect: { one: number, two: number } = b.intersect('test'); +>intersect : { one: number; two: number; } +>one : number +>two : number +>b.intersect('test') : { one: number; two: number; } +>b.intersect : (id: string) => { one: number; two: number; } +>b : Both +>intersect : (id: string) => { one: number; two: number; } +>'test' : "test" + +const overloadA: { one: number } = b.overload('test'); +>overloadA : { one: number; } +>one : number +>b.overload('test') : { one: number; } +>b.overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>b : Both +>overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>'test' : "test" + +const overloadB: { two: number } = b.overload(4); +>overloadB : { two: number; } +>two : number +>b.overload(4) : { two: number; } +>b.overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>b : Both +>overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>4 : 4 + +const bAs: One & Two = b; +>bAs : One & Two +>b : Both + +const asIntersect: { one: number, two: number } = bAs.intersect('test'); +>asIntersect : { one: number; two: number; } +>one : number +>two : number +>bAs.intersect('test') : { one: number; } & { two: number; } +>bAs.intersect : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>bAs : One & Two +>intersect : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>'test' : "test" + +const asOverloadA: { one: number } = bAs.overload('test'); +>asOverloadA : { one: number; } +>one : number +>bAs.overload('test') : { one: number; } +>bAs.overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) +>bAs : One & Two +>overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) +>'test' : "test" + +const asOverloadB: { two: number } = bAs.overload(4); +>asOverloadB : { two: number; } +>two : number +>bAs.overload(4) : { two: number; } +>bAs.overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) +>bAs : One & Two +>overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) +>4 : 4 + diff --git a/tests/cases/compiler/intersectionOfCallsWithSameParameters.ts b/tests/cases/compiler/intersectionOfCallsWithSameParameters.ts new file mode 100644 index 0000000000000..aad6b38438708 --- /dev/null +++ b/tests/cases/compiler/intersectionOfCallsWithSameParameters.ts @@ -0,0 +1,38 @@ +// @strict: true + +interface One { + overload(id: string): { one: number }; + intersect(id: string): { one: number }; +} + +interface Two { + overload(id: number): { two: number }; + intersect(id: string): { two: number }; +} + +class Both implements One, Two { + overload(id: number): { two: number }; + overload(id: string): { one: number }; + overload(id: string | number): { one: number, two: number } { + return { + one: 1, + two: 2 + }; + } + + intersect(id: string): { one: number, two: number } { + return { + one: 1, + two: 2 + }; + } +} + +const b = new Both(); +const intersect: { one: number, two: number } = b.intersect('test'); +const overloadA: { one: number } = b.overload('test'); +const overloadB: { two: number } = b.overload(4); +const bAs: One & Two = b; +const asIntersect: { one: number, two: number } = bAs.intersect('test'); +const asOverloadA: { one: number } = bAs.overload('test'); +const asOverloadB: { two: number } = bAs.overload(4); From 35f064d94a93245327635dd06af0dc7f2daf085d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 24 Mar 2019 22:57:48 +1100 Subject: [PATCH 4/4] Require identical type parameters to intersect call signatures. --- src/compiler/checker.ts | 51 ++- ...ectionOfCallsWithSameParameters.errors.txt | 61 ++++ .../intersectionOfCallsWithSameParameters.js | 99 +++--- ...ersectionOfCallsWithSameParameters.symbols | 312 +++++++++++------- ...ntersectionOfCallsWithSameParameters.types | 239 +++++++++----- ...oTypeGuardsAllowUseOfFunctionReturnType.js | 38 +++ ...GuardsAllowUseOfFunctionReturnType.symbols | 69 ++++ ...peGuardsAllowUseOfFunctionReturnType.types | 77 +++++ .../intersectionOfCallsWithSameParameters.ts | 61 ++-- ...oTypeGuardsAllowUseOfFunctionReturnType.ts | 23 ++ 10 files changed, 730 insertions(+), 300 deletions(-) create mode 100644 tests/baselines/reference/intersectionOfCallsWithSameParameters.errors.txt create mode 100644 tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.js create mode 100644 tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.symbols create mode 100644 tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.types create mode 100644 tests/cases/compiler/twoTypeGuardsAllowUseOfFunctionReturnType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 949ad2aaa7d09..22f554dc4ce08 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7095,25 +7095,58 @@ namespace ts { setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } + function callSignaturesAreIdenticalExceptReturnType(a: Signature, b: Signature): boolean { + const parametersA = a.parameters; + const parametersB = b.parameters; + + if (parametersA.length !== parametersB.length) { + return false; + } + + if (!every(parametersA, (parameter, i) => isTypeIdenticalTo(getTypeOfParameter(parameter), getTypeOfParameter(parametersB[i])))) { + return false; + } + + // Parameter types are identical, now check type parameters. + const typesA = a.typeParameters; + const typesB = b.typeParameters; + + if (typesA === undefined || typesB === undefined) { + return typesA === typesB; + } + + if (typesA.length !== typesB.length) { + return false; + } + + return every(typesA, (parameter, i) => { + const constraintA = getConstraintOfTypeParameter(parameter); + const constraintB = getConstraintOfTypeParameter(typesB[i]); + const sameConstraints = (constraintA === undefined && constraintB === undefined) || + (constraintA !== undefined && constraintB !== undefined && isTypeIdenticalTo(constraintA, constraintB)); + + const defaultA = parameter.default; + const defaultB = typesB[i].default; + const sameDefaults = (defaultA === undefined && defaultB === undefined) || + (defaultA !== undefined && defaultB !== undefined && isTypeIdenticalTo(defaultA, defaultB)); + + return sameConstraints && sameDefaults; + }); + } + function intersectCallSignatures(callSignatures: ReadonlyArray): ReadonlyArray { if (callSignatures.length === 0) { return callSignatures; } - // Overloads that differ only be return type make no sense. So: - // 1. Group all the call signatures by their parameter types. Each group is an overload. + // Overloads that differ only by return type are not usable, so: + // 1. Group all the call signatures by their type parameters + parameter types. Each group is an overload. // 2. Create a new signature for each group where the return type is the intersection // of the return types of the signatures in that group. const groups: Signature[][] = []; callSignatures.forEach((callSignature) => { - const matchingGroup = find(groups, (group) => { - const candidate = group[0]; - const candidateParameters = candidate.parameters; - const thisCallParameters = callSignature.parameters; - return candidateParameters.length === thisCallParameters.length && - every(thisCallParameters, (parameter, index) => isTypeIdenticalTo(getTypeOfParameter(parameter), getTypeOfParameter(candidateParameters[index]))); - }); + const matchingGroup = find(groups, (group) => callSignaturesAreIdenticalExceptReturnType(callSignature, group[0])); if (matchingGroup === undefined) { groups.push([callSignature]); } diff --git a/tests/baselines/reference/intersectionOfCallsWithSameParameters.errors.txt b/tests/baselines/reference/intersectionOfCallsWithSameParameters.errors.txt new file mode 100644 index 0000000000000..f33695ffd3852 --- /dev/null +++ b/tests/baselines/reference/intersectionOfCallsWithSameParameters.errors.txt @@ -0,0 +1,61 @@ +tests/cases/compiler/intersectionOfCallsWithSameParameters.ts(38,7): error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. +tests/cases/compiler/intersectionOfCallsWithSameParameters.ts(39,7): error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. +tests/cases/compiler/intersectionOfCallsWithSameParameters.ts(40,7): error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. +tests/cases/compiler/intersectionOfCallsWithSameParameters.ts(41,7): error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. + + +==== tests/cases/compiler/intersectionOfCallsWithSameParameters.ts (4 errors) ==== + interface One { + differentParameterType(id: string): { one: number }; + differentNumberOfParameters(id: string): { one: number }; + differentTypeParameterDefault(id: string): { one: number }; + differentTypeParameterConstraint(id: string): { one: number }; + + same1(id: string): { one: number }; + same2(id: string): { one: number }; + same3(id: string): { one: number }; + same4(id: string): { one: number }; + same5(id: string): { one: number }; + } + + interface Two { + differentParameterType(id: number): { two: number }; + differentNumberOfParameters(id: string, second: string): { two: number }; + differentTypeParameterDefault(id: string): { two: number }; + differentTypeParameterConstraint(id: string): { two: number }; + + same1(id: string): { two: number }; + same2(id: string): { two: number }; + same3(id: string): { two: number }; + same4(id: string): { two: number }; + same5(id: string): { two: number }; + } + + const i: One & Two = {}; + + // These lines should type check; the return type should be intersected. + const same1: { one: number, two: number } = i.same1('test'); + const same2: { one: number, two: number } = i.same2('test'); + const same3: { one: number, two: number } = i.same3<{ one:number }>('test'); + const same4: { one: number, two: number } = i.same4('test'); + const same5: { one: number, two: number } = i.same5<{ one:number }, string>('test'); + + // These lines should not, because the functions should become overloads rather + // than the return types intersected. + const differentParameterType: { one: number, two: number } = i.differentParameterType('test'); + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. +!!! related TS2728 tests/cases/compiler/intersectionOfCallsWithSameParameters.ts:38:46: 'two' is declared here. + const differentNumberOfParameters: { one: number, two: number } = i.differentNumberOfParameters('test'); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. +!!! related TS2728 tests/cases/compiler/intersectionOfCallsWithSameParameters.ts:39:51: 'two' is declared here. + const differentTypeParameterDefault: { one: number, two: number } = i.differentTypeParameterDefault('test'); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. +!!! related TS2728 tests/cases/compiler/intersectionOfCallsWithSameParameters.ts:40:53: 'two' is declared here. + const differentTypeParameterConstraint: { one: number, two: number } = i.differentTypeParameterConstraint<{ one: number }>('test'); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2741: Property 'two' is missing in type '{ one: number; }' but required in type '{ one: number; two: number; }'. +!!! related TS2728 tests/cases/compiler/intersectionOfCallsWithSameParameters.ts:41:56: 'two' is declared here. + \ No newline at end of file diff --git a/tests/baselines/reference/intersectionOfCallsWithSameParameters.js b/tests/baselines/reference/intersectionOfCallsWithSameParameters.js index 87ce4358342f5..e2c82c0f7d1c3 100644 --- a/tests/baselines/reference/intersectionOfCallsWithSameParameters.js +++ b/tests/baselines/reference/intersectionOfCallsWithSameParameters.js @@ -1,66 +1,59 @@ //// [intersectionOfCallsWithSameParameters.ts] interface One { - overload(id: string): { one: number }; - intersect(id: string): { one: number }; + differentParameterType(id: string): { one: number }; + differentNumberOfParameters(id: string): { one: number }; + differentTypeParameterDefault(id: string): { one: number }; + differentTypeParameterConstraint(id: string): { one: number }; + + same1(id: string): { one: number }; + same2(id: string): { one: number }; + same3(id: string): { one: number }; + same4(id: string): { one: number }; + same5(id: string): { one: number }; } interface Two { - overload(id: number): { two: number }; - intersect(id: string): { two: number }; + differentParameterType(id: number): { two: number }; + differentNumberOfParameters(id: string, second: string): { two: number }; + differentTypeParameterDefault(id: string): { two: number }; + differentTypeParameterConstraint(id: string): { two: number }; + + same1(id: string): { two: number }; + same2(id: string): { two: number }; + same3(id: string): { two: number }; + same4(id: string): { two: number }; + same5(id: string): { two: number }; } -class Both implements One, Two { - overload(id: number): { two: number }; - overload(id: string): { one: number }; - overload(id: string | number): { one: number, two: number } { - return { - one: 1, - two: 2 - }; - } +const i: One & Two = {}; - intersect(id: string): { one: number, two: number } { - return { - one: 1, - two: 2 - }; - } -} +// These lines should type check; the return type should be intersected. +const same1: { one: number, two: number } = i.same1('test'); +const same2: { one: number, two: number } = i.same2('test'); +const same3: { one: number, two: number } = i.same3<{ one:number }>('test'); +const same4: { one: number, two: number } = i.same4('test'); +const same5: { one: number, two: number } = i.same5<{ one:number }, string>('test'); -const b = new Both(); -const intersect: { one: number, two: number } = b.intersect('test'); -const overloadA: { one: number } = b.overload('test'); -const overloadB: { two: number } = b.overload(4); -const bAs: One & Two = b; -const asIntersect: { one: number, two: number } = bAs.intersect('test'); -const asOverloadA: { one: number } = bAs.overload('test'); -const asOverloadB: { two: number } = bAs.overload(4); +// These lines should not, because the functions should become overloads rather +// than the return types intersected. +const differentParameterType: { one: number, two: number } = i.differentParameterType('test'); +const differentNumberOfParameters: { one: number, two: number } = i.differentNumberOfParameters('test'); +const differentTypeParameterDefault: { one: number, two: number } = i.differentTypeParameterDefault('test'); +const differentTypeParameterConstraint: { one: number, two: number } = i.differentTypeParameterConstraint<{ one: number }>('test'); //// [intersectionOfCallsWithSameParameters.js] "use strict"; -var Both = /** @class */ (function () { - function Both() { - } - Both.prototype.overload = function (id) { - return { - one: 1, - two: 2 - }; - }; - Both.prototype.intersect = function (id) { - return { - one: 1, - two: 2 - }; - }; - return Both; -}()); -var b = new Both(); -var intersect = b.intersect('test'); -var overloadA = b.overload('test'); -var overloadB = b.overload(4); -var bAs = b; -var asIntersect = bAs.intersect('test'); -var asOverloadA = bAs.overload('test'); -var asOverloadB = bAs.overload(4); +var i = {}; +// These lines should type check; the return type should be intersected. +var same1 = i.same1('test'); +var same2 = i.same2('test'); +var same3 = i.same3('test'); +var same4 = i.same4('test'); +var same5 = i.same5('test'); +// These lines should not, because the functions should become overloads rather +// than the return types intersected. +var differentParameterType = i.differentParameterType('test'); +var differentNumberOfParameters = i.differentNumberOfParameters('test'); +var differentTypeParameterDefault = i.differentTypeParameterDefault('test'); +var differentTypeParameterConstraint = i.differentTypeParameterConstraint('test'); diff --git a/tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols b/tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols index 1f9bf9134c28d..9afd35400564b 100644 --- a/tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols +++ b/tests/baselines/reference/intersectionOfCallsWithSameParameters.symbols @@ -2,130 +2,202 @@ interface One { >One : Symbol(One, Decl(intersectionOfCallsWithSameParameters.ts, 0, 0)) - overload(id: string): { one: number }; ->overload : Symbol(One.overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 1, 13)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 1, 27)) - - intersect(id: string): { one: number }; ->intersect : Symbol(One.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 1, 42)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 2, 14)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 2, 28)) + differentParameterType(id: string): { one: number }; +>differentParameterType : Symbol(One.differentParameterType, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 1, 27)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 1, 41)) + + differentNumberOfParameters(id: string): { one: number }; +>differentNumberOfParameters : Symbol(One.differentNumberOfParameters, Decl(intersectionOfCallsWithSameParameters.ts, 1, 56)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 2, 32)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 2, 46)) + + differentTypeParameterDefault(id: string): { one: number }; +>differentTypeParameterDefault : Symbol(One.differentTypeParameterDefault, Decl(intersectionOfCallsWithSameParameters.ts, 2, 61)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 3, 34)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 3, 46)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 3, 60)) + + differentTypeParameterConstraint(id: string): { one: number }; +>differentTypeParameterConstraint : Symbol(One.differentTypeParameterConstraint, Decl(intersectionOfCallsWithSameParameters.ts, 3, 75)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 4, 37)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 4, 48)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 4, 64)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 4, 78)) + + same1(id: string): { one: number }; +>same1 : Symbol(One.same1, Decl(intersectionOfCallsWithSameParameters.ts, 4, 93)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 6, 10)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 6, 24)) + + same2(id: string): { one: number }; +>same2 : Symbol(One.same2, Decl(intersectionOfCallsWithSameParameters.ts, 6, 39)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 7, 10)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 7, 13)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 7, 27)) + + same3(id: string): { one: number }; +>same3 : Symbol(One.same3, Decl(intersectionOfCallsWithSameParameters.ts, 7, 42)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 8, 10)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 8, 21)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 8, 37)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 8, 51)) + + same4(id: string): { one: number }; +>same4 : Symbol(One.same4, Decl(intersectionOfCallsWithSameParameters.ts, 8, 66)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 9, 10)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 9, 22)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 9, 36)) + + same5(id: string): { one: number }; +>same5 : Symbol(One.same5, Decl(intersectionOfCallsWithSameParameters.ts, 9, 51)) +>T1 : Symbol(T1, Decl(intersectionOfCallsWithSameParameters.ts, 10, 10)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 10, 22)) +>T2 : Symbol(T2, Decl(intersectionOfCallsWithSameParameters.ts, 10, 37)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 10, 51)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 10, 65)) } interface Two { ->Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 3, 1)) - - overload(id: number): { two: number }; ->overload : Symbol(Two.overload, Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 6, 13)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 6, 27)) - - intersect(id: string): { two: number }; ->intersect : Symbol(Two.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 6, 42)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 7, 14)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 7, 28)) -} - -class Both implements One, Two { ->Both : Symbol(Both, Decl(intersectionOfCallsWithSameParameters.ts, 8, 1)) ->One : Symbol(One, Decl(intersectionOfCallsWithSameParameters.ts, 0, 0)) ->Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 3, 1)) - - overload(id: number): { two: number }; ->overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 11, 13)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 11, 27)) - - overload(id: string): { one: number }; ->overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 12, 13)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 12, 27)) - - overload(id: string | number): { one: number, two: number } { ->overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 13, 13)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 13, 36)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 13, 49)) - - return { - one: 1, ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 14, 16)) - - two: 2 ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 15, 19)) - - }; - } - - intersect(id: string): { one: number, two: number } { ->intersect : Symbol(Both.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 18, 5)) ->id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 20, 14)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 20, 28)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 20, 41)) - - return { - one: 1, ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 21, 16)) - - two: 2 ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 22, 19)) - - }; - } +>Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 11, 1)) + + differentParameterType(id: number): { two: number }; +>differentParameterType : Symbol(Two.differentParameterType, Decl(intersectionOfCallsWithSameParameters.ts, 13, 15)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 14, 27)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 14, 41)) + + differentNumberOfParameters(id: string, second: string): { two: number }; +>differentNumberOfParameters : Symbol(Two.differentNumberOfParameters, Decl(intersectionOfCallsWithSameParameters.ts, 14, 56)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 15, 32)) +>second : Symbol(second, Decl(intersectionOfCallsWithSameParameters.ts, 15, 43)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 15, 62)) + + differentTypeParameterDefault(id: string): { two: number }; +>differentTypeParameterDefault : Symbol(Two.differentTypeParameterDefault, Decl(intersectionOfCallsWithSameParameters.ts, 15, 77)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 16, 34)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 16, 46)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 16, 60)) + + differentTypeParameterConstraint(id: string): { two: number }; +>differentTypeParameterConstraint : Symbol(Two.differentTypeParameterConstraint, Decl(intersectionOfCallsWithSameParameters.ts, 16, 75)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 17, 37)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 17, 48)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 17, 64)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 17, 78)) + + same1(id: string): { two: number }; +>same1 : Symbol(Two.same1, Decl(intersectionOfCallsWithSameParameters.ts, 17, 93)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 19, 10)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 19, 24)) + + same2(id: string): { two: number }; +>same2 : Symbol(Two.same2, Decl(intersectionOfCallsWithSameParameters.ts, 19, 39)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 20, 10)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 20, 13)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 20, 27)) + + same3(id: string): { two: number }; +>same3 : Symbol(Two.same3, Decl(intersectionOfCallsWithSameParameters.ts, 20, 42)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 21, 10)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 21, 21)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 21, 37)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 21, 51)) + + same4(id: string): { two: number }; +>same4 : Symbol(Two.same4, Decl(intersectionOfCallsWithSameParameters.ts, 21, 66)) +>T : Symbol(T, Decl(intersectionOfCallsWithSameParameters.ts, 22, 10)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 22, 22)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 22, 36)) + + same5(id: string): { two: number }; +>same5 : Symbol(Two.same5, Decl(intersectionOfCallsWithSameParameters.ts, 22, 51)) +>T1 : Symbol(T1, Decl(intersectionOfCallsWithSameParameters.ts, 23, 10)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 23, 22)) +>T2 : Symbol(T2, Decl(intersectionOfCallsWithSameParameters.ts, 23, 37)) +>id : Symbol(id, Decl(intersectionOfCallsWithSameParameters.ts, 23, 51)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 23, 65)) } -const b = new Both(); ->b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) ->Both : Symbol(Both, Decl(intersectionOfCallsWithSameParameters.ts, 8, 1)) - -const intersect: { one: number, two: number } = b.intersect('test'); ->intersect : Symbol(intersect, Decl(intersectionOfCallsWithSameParameters.ts, 29, 5)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 29, 18)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 29, 31)) ->b.intersect : Symbol(Both.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 18, 5)) ->b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) ->intersect : Symbol(Both.intersect, Decl(intersectionOfCallsWithSameParameters.ts, 18, 5)) - -const overloadA: { one: number } = b.overload('test'); ->overloadA : Symbol(overloadA, Decl(intersectionOfCallsWithSameParameters.ts, 30, 5)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 30, 18)) ->b.overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) ->b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) ->overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) - -const overloadB: { two: number } = b.overload(4); ->overloadB : Symbol(overloadB, Decl(intersectionOfCallsWithSameParameters.ts, 31, 5)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 31, 18)) ->b.overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) ->b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) ->overload : Symbol(Both.overload, Decl(intersectionOfCallsWithSameParameters.ts, 10, 32), Decl(intersectionOfCallsWithSameParameters.ts, 11, 42), Decl(intersectionOfCallsWithSameParameters.ts, 12, 42)) - -const bAs: One & Two = b; ->bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) +const i: One & Two = {}; +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) >One : Symbol(One, Decl(intersectionOfCallsWithSameParameters.ts, 0, 0)) ->Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 3, 1)) ->b : Symbol(b, Decl(intersectionOfCallsWithSameParameters.ts, 28, 5)) - -const asIntersect: { one: number, two: number } = bAs.intersect('test'); ->asIntersect : Symbol(asIntersect, Decl(intersectionOfCallsWithSameParameters.ts, 33, 5)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 33, 20)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 33, 33)) ->bAs.intersect : Symbol(intersect, Decl(intersectionOfCallsWithSameParameters.ts, 1, 42), Decl(intersectionOfCallsWithSameParameters.ts, 6, 42)) ->bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) ->intersect : Symbol(intersect, Decl(intersectionOfCallsWithSameParameters.ts, 1, 42), Decl(intersectionOfCallsWithSameParameters.ts, 6, 42)) - -const asOverloadA: { one: number } = bAs.overload('test'); ->asOverloadA : Symbol(asOverloadA, Decl(intersectionOfCallsWithSameParameters.ts, 34, 5)) ->one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 34, 20)) ->bAs.overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) ->bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) ->overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) - -const asOverloadB: { two: number } = bAs.overload(4); ->asOverloadB : Symbol(asOverloadB, Decl(intersectionOfCallsWithSameParameters.ts, 35, 5)) ->two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 35, 20)) ->bAs.overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) ->bAs : Symbol(bAs, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) ->overload : Symbol(overload, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 5, 15)) +>Two : Symbol(Two, Decl(intersectionOfCallsWithSameParameters.ts, 11, 1)) + +// These lines should type check; the return type should be intersected. +const same1: { one: number, two: number } = i.same1('test'); +>same1 : Symbol(same1, Decl(intersectionOfCallsWithSameParameters.ts, 29, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 29, 14)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 29, 27)) +>i.same1 : Symbol(same1, Decl(intersectionOfCallsWithSameParameters.ts, 4, 93), Decl(intersectionOfCallsWithSameParameters.ts, 17, 93)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>same1 : Symbol(same1, Decl(intersectionOfCallsWithSameParameters.ts, 4, 93), Decl(intersectionOfCallsWithSameParameters.ts, 17, 93)) + +const same2: { one: number, two: number } = i.same2('test'); +>same2 : Symbol(same2, Decl(intersectionOfCallsWithSameParameters.ts, 30, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 30, 14)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 30, 27)) +>i.same2 : Symbol(same2, Decl(intersectionOfCallsWithSameParameters.ts, 6, 39), Decl(intersectionOfCallsWithSameParameters.ts, 19, 39)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>same2 : Symbol(same2, Decl(intersectionOfCallsWithSameParameters.ts, 6, 39), Decl(intersectionOfCallsWithSameParameters.ts, 19, 39)) + +const same3: { one: number, two: number } = i.same3<{ one:number }>('test'); +>same3 : Symbol(same3, Decl(intersectionOfCallsWithSameParameters.ts, 31, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 31, 14)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 31, 27)) +>i.same3 : Symbol(same3, Decl(intersectionOfCallsWithSameParameters.ts, 7, 42), Decl(intersectionOfCallsWithSameParameters.ts, 20, 42)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>same3 : Symbol(same3, Decl(intersectionOfCallsWithSameParameters.ts, 7, 42), Decl(intersectionOfCallsWithSameParameters.ts, 20, 42)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 31, 53)) + +const same4: { one: number, two: number } = i.same4('test'); +>same4 : Symbol(same4, Decl(intersectionOfCallsWithSameParameters.ts, 32, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 32, 14)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 32, 27)) +>i.same4 : Symbol(same4, Decl(intersectionOfCallsWithSameParameters.ts, 8, 66), Decl(intersectionOfCallsWithSameParameters.ts, 21, 66)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>same4 : Symbol(same4, Decl(intersectionOfCallsWithSameParameters.ts, 8, 66), Decl(intersectionOfCallsWithSameParameters.ts, 21, 66)) + +const same5: { one: number, two: number } = i.same5<{ one:number }, string>('test'); +>same5 : Symbol(same5, Decl(intersectionOfCallsWithSameParameters.ts, 33, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 33, 14)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 33, 27)) +>i.same5 : Symbol(same5, Decl(intersectionOfCallsWithSameParameters.ts, 9, 51), Decl(intersectionOfCallsWithSameParameters.ts, 22, 51)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>same5 : Symbol(same5, Decl(intersectionOfCallsWithSameParameters.ts, 9, 51), Decl(intersectionOfCallsWithSameParameters.ts, 22, 51)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 33, 53)) + +// These lines should not, because the functions should become overloads rather +// than the return types intersected. +const differentParameterType: { one: number, two: number } = i.differentParameterType('test'); +>differentParameterType : Symbol(differentParameterType, Decl(intersectionOfCallsWithSameParameters.ts, 37, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 37, 31)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 37, 44)) +>i.differentParameterType : Symbol(differentParameterType, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 13, 15)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>differentParameterType : Symbol(differentParameterType, Decl(intersectionOfCallsWithSameParameters.ts, 0, 15), Decl(intersectionOfCallsWithSameParameters.ts, 13, 15)) + +const differentNumberOfParameters: { one: number, two: number } = i.differentNumberOfParameters('test'); +>differentNumberOfParameters : Symbol(differentNumberOfParameters, Decl(intersectionOfCallsWithSameParameters.ts, 38, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 38, 36)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 38, 49)) +>i.differentNumberOfParameters : Symbol(differentNumberOfParameters, Decl(intersectionOfCallsWithSameParameters.ts, 1, 56), Decl(intersectionOfCallsWithSameParameters.ts, 14, 56)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>differentNumberOfParameters : Symbol(differentNumberOfParameters, Decl(intersectionOfCallsWithSameParameters.ts, 1, 56), Decl(intersectionOfCallsWithSameParameters.ts, 14, 56)) + +const differentTypeParameterDefault: { one: number, two: number } = i.differentTypeParameterDefault('test'); +>differentTypeParameterDefault : Symbol(differentTypeParameterDefault, Decl(intersectionOfCallsWithSameParameters.ts, 39, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 39, 38)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 39, 51)) +>i.differentTypeParameterDefault : Symbol(differentTypeParameterDefault, Decl(intersectionOfCallsWithSameParameters.ts, 2, 61), Decl(intersectionOfCallsWithSameParameters.ts, 15, 77)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>differentTypeParameterDefault : Symbol(differentTypeParameterDefault, Decl(intersectionOfCallsWithSameParameters.ts, 2, 61), Decl(intersectionOfCallsWithSameParameters.ts, 15, 77)) + +const differentTypeParameterConstraint: { one: number, two: number } = i.differentTypeParameterConstraint<{ one: number }>('test'); +>differentTypeParameterConstraint : Symbol(differentTypeParameterConstraint, Decl(intersectionOfCallsWithSameParameters.ts, 40, 5)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 40, 41)) +>two : Symbol(two, Decl(intersectionOfCallsWithSameParameters.ts, 40, 54)) +>i.differentTypeParameterConstraint : Symbol(differentTypeParameterConstraint, Decl(intersectionOfCallsWithSameParameters.ts, 3, 75), Decl(intersectionOfCallsWithSameParameters.ts, 16, 75)) +>i : Symbol(i, Decl(intersectionOfCallsWithSameParameters.ts, 26, 5)) +>differentTypeParameterConstraint : Symbol(differentTypeParameterConstraint, Decl(intersectionOfCallsWithSameParameters.ts, 3, 75), Decl(intersectionOfCallsWithSameParameters.ts, 16, 75)) +>one : Symbol(one, Decl(intersectionOfCallsWithSameParameters.ts, 40, 107)) diff --git a/tests/baselines/reference/intersectionOfCallsWithSameParameters.types b/tests/baselines/reference/intersectionOfCallsWithSameParameters.types index 96238b472bb23..9406022cdc472 100644 --- a/tests/baselines/reference/intersectionOfCallsWithSameParameters.types +++ b/tests/baselines/reference/intersectionOfCallsWithSameParameters.types @@ -1,144 +1,203 @@ === tests/cases/compiler/intersectionOfCallsWithSameParameters.ts === interface One { - overload(id: string): { one: number }; ->overload : (id: string) => { one: number; } + differentParameterType(id: string): { one: number }; +>differentParameterType : (id: string) => { one: number; } >id : string >one : number - intersect(id: string): { one: number }; ->intersect : (id: string) => { one: number; } + differentNumberOfParameters(id: string): { one: number }; +>differentNumberOfParameters : (id: string) => { one: number; } +>id : string +>one : number + + differentTypeParameterDefault(id: string): { one: number }; +>differentTypeParameterDefault : (id: string) => { one: number; } +>id : string +>one : number + + differentTypeParameterConstraint(id: string): { one: number }; +>differentTypeParameterConstraint : (id: string) => { one: number; } +>one : number +>id : string +>one : number + + same1(id: string): { one: number }; +>same1 : (id: string) => { one: number; } +>id : string +>one : number + + same2(id: string): { one: number }; +>same2 : (id: string) => { one: number; } +>id : string +>one : number + + same3(id: string): { one: number }; +>same3 : (id: string) => { one: number; } +>one : number +>id : string +>one : number + + same4(id: string): { one: number }; +>same4 : (id: string) => { one: number; } +>id : string +>one : number + + same5(id: string): { one: number }; +>same5 : (id: string) => { one: number; } +>one : number >id : string >one : number } interface Two { - overload(id: number): { two: number }; ->overload : (id: number) => { two: number; } + differentParameterType(id: number): { two: number }; +>differentParameterType : (id: number) => { two: number; } >id : number >two : number - intersect(id: string): { two: number }; ->intersect : (id: string) => { two: number; } + differentNumberOfParameters(id: string, second: string): { two: number }; +>differentNumberOfParameters : (id: string, second: string) => { two: number; } >id : string +>second : string >two : number -} - -class Both implements One, Two { ->Both : Both - overload(id: number): { two: number }; ->overload : { (id: number): { two: number; }; (id: string): { one: number; }; } ->id : number + differentTypeParameterDefault(id: string): { two: number }; +>differentTypeParameterDefault : (id: string) => { two: number; } +>id : string >two : number - overload(id: string): { one: number }; ->overload : { (id: number): { two: number; }; (id: string): { one: number; }; } + differentTypeParameterConstraint(id: string): { two: number }; +>differentTypeParameterConstraint : (id: string) => { two: number; } +>two : number >id : string ->one : number +>two : number - overload(id: string | number): { one: number, two: number } { ->overload : { (id: number): { two: number; }; (id: string): { one: number; }; } ->id : string | number ->one : number + same1(id: string): { two: number }; +>same1 : (id: string) => { two: number; } +>id : string >two : number - return { ->{ one: 1, two: 2 } : { one: number; two: number; } + same2(id: string): { two: number }; +>same2 : (id: string) => { two: number; } +>id : string +>two : number - one: 1, + same3(id: string): { two: number }; +>same3 : (id: string) => { two: number; } >one : number ->1 : 1 - - two: 2 +>id : string >two : number ->2 : 2 - }; - } - - intersect(id: string): { one: number, two: number } { ->intersect : (id: string) => { one: number; two: number; } + same4(id: string): { two: number }; +>same4 : (id: string) => { two: number; } >id : string +>two : number + + same5(id: string): { two: number }; +>same5 : (id: string) => { two: number; } >one : number +>id : string >two : number +} - return { ->{ one: 1, two: 2 } : { one: number; two: number; } +const i: One & Two = {}; +>i : One & Two +>{} : any +>{} : {} - one: 1, +// These lines should type check; the return type should be intersected. +const same1: { one: number, two: number } = i.same1('test'); +>same1 : { one: number; two: number; } >one : number ->1 : 1 - - two: 2 >two : number ->2 : 2 - - }; - } -} +>i.same1('test') : { one: number; } & { two: number; } +>i.same1 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i : One & Two +>same1 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>'test' : "test" -const b = new Both(); ->b : Both ->new Both() : Both ->Both : typeof Both +const same2: { one: number, two: number } = i.same2('test'); +>same2 : { one: number; two: number; } +>one : number +>two : number +>i.same2('test') : { one: number; } & { two: number; } +>i.same2 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i : One & Two +>same2 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>'test' : "test" -const intersect: { one: number, two: number } = b.intersect('test'); ->intersect : { one: number; two: number; } +const same3: { one: number, two: number } = i.same3<{ one:number }>('test'); +>same3 : { one: number; two: number; } >one : number >two : number ->b.intersect('test') : { one: number; two: number; } ->b.intersect : (id: string) => { one: number; two: number; } ->b : Both ->intersect : (id: string) => { one: number; two: number; } +>i.same3<{ one:number }>('test') : { one: number; } & { two: number; } +>i.same3 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i : One & Two +>same3 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>one : number >'test' : "test" -const overloadA: { one: number } = b.overload('test'); ->overloadA : { one: number; } +const same4: { one: number, two: number } = i.same4('test'); +>same4 : { one: number; two: number; } >one : number ->b.overload('test') : { one: number; } ->b.overload : { (id: number): { two: number; }; (id: string): { one: number; }; } ->b : Both ->overload : { (id: number): { two: number; }; (id: string): { one: number; }; } +>two : number +>i.same4('test') : { one: number; } & { two: number; } +>i.same4 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i : One & Two +>same4 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) >'test' : "test" -const overloadB: { two: number } = b.overload(4); ->overloadB : { two: number; } +const same5: { one: number, two: number } = i.same5<{ one:number }, string>('test'); +>same5 : { one: number; two: number; } +>one : number >two : number ->b.overload(4) : { two: number; } ->b.overload : { (id: number): { two: number; }; (id: string): { one: number; }; } ->b : Both ->overload : { (id: number): { two: number; }; (id: string): { one: number; }; } ->4 : 4 +>i.same5<{ one:number }, string>('test') : { one: number; } & { two: number; } +>i.same5 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i : One & Two +>same5 : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>one : number +>'test' : "test" -const bAs: One & Two = b; ->bAs : One & Two ->b : Both +// These lines should not, because the functions should become overloads rather +// than the return types intersected. +const differentParameterType: { one: number, two: number } = i.differentParameterType('test'); +>differentParameterType : { one: number; two: number; } +>one : number +>two : number +>i.differentParameterType('test') : { one: number; } +>i.differentParameterType : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) +>i : One & Two +>differentParameterType : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) +>'test' : "test" -const asIntersect: { one: number, two: number } = bAs.intersect('test'); ->asIntersect : { one: number; two: number; } +const differentNumberOfParameters: { one: number, two: number } = i.differentNumberOfParameters('test'); +>differentNumberOfParameters : { one: number; two: number; } >one : number >two : number ->bAs.intersect('test') : { one: number; } & { two: number; } ->bAs.intersect : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) ->bAs : One & Two ->intersect : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i.differentNumberOfParameters('test') : { one: number; } +>i.differentNumberOfParameters : ((id: string) => { one: number; }) & ((id: string, second: string) => { two: number; }) +>i : One & Two +>differentNumberOfParameters : ((id: string) => { one: number; }) & ((id: string, second: string) => { two: number; }) >'test' : "test" -const asOverloadA: { one: number } = bAs.overload('test'); ->asOverloadA : { one: number; } +const differentTypeParameterDefault: { one: number, two: number } = i.differentTypeParameterDefault('test'); +>differentTypeParameterDefault : { one: number; two: number; } >one : number ->bAs.overload('test') : { one: number; } ->bAs.overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) ->bAs : One & Two ->overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) +>two : number +>i.differentTypeParameterDefault('test') : { one: number; } +>i.differentTypeParameterDefault : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i : One & Two +>differentTypeParameterDefault : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) >'test' : "test" -const asOverloadB: { two: number } = bAs.overload(4); ->asOverloadB : { two: number; } +const differentTypeParameterConstraint: { one: number, two: number } = i.differentTypeParameterConstraint<{ one: number }>('test'); +>differentTypeParameterConstraint : { one: number; two: number; } +>one : number >two : number ->bAs.overload(4) : { two: number; } ->bAs.overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) ->bAs : One & Two ->overload : ((id: string) => { one: number; }) & ((id: number) => { two: number; }) ->4 : 4 +>i.differentTypeParameterConstraint<{ one: number }>('test') : { one: number; } +>i.differentTypeParameterConstraint : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>i : One & Two +>differentTypeParameterConstraint : ((id: string) => { one: number; }) & ((id: string) => { two: number; }) +>one : number +>'test' : "test" diff --git a/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.js b/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.js new file mode 100644 index 0000000000000..6ab75a0d2bee4 --- /dev/null +++ b/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.js @@ -0,0 +1,38 @@ +//// [twoTypeGuardsAllowUseOfFunctionReturnType.ts] +interface Opacity { + readonly opacity: number; + getStratum(id: string): { opacity: number }; +} + +interface Layer { + readonly layer: string; + getStratum(id: string): { layer: string }; +} + +function hasOpacity(x: any): x is Opacity { + return 'opacity' in x; +} + +function hasLayer(x: any): x is Layer { + return 'layer' in x; +} + +const foo = {}; +if (hasOpacity(foo) && hasLayer(foo)) { + foo.getStratum('user').opacity = 0.5; + foo.getStratum('user').layer = 'test'; +} + + +//// [twoTypeGuardsAllowUseOfFunctionReturnType.js] +function hasOpacity(x) { + return 'opacity' in x; +} +function hasLayer(x) { + return 'layer' in x; +} +var foo = {}; +if (hasOpacity(foo) && hasLayer(foo)) { + foo.getStratum('user').opacity = 0.5; + foo.getStratum('user').layer = 'test'; +} diff --git a/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.symbols b/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.symbols new file mode 100644 index 0000000000000..a5a2cc9ae624d --- /dev/null +++ b/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.symbols @@ -0,0 +1,69 @@ +=== tests/cases/compiler/twoTypeGuardsAllowUseOfFunctionReturnType.ts === +interface Opacity { +>Opacity : Symbol(Opacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 0, 0)) + + readonly opacity: number; +>opacity : Symbol(Opacity.opacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 0, 19)) + + getStratum(id: string): { opacity: number }; +>getStratum : Symbol(Opacity.getStratum, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 1, 29)) +>id : Symbol(id, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 2, 15)) +>opacity : Symbol(opacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 2, 29)) +} + +interface Layer { +>Layer : Symbol(Layer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 3, 1)) + + readonly layer: string; +>layer : Symbol(Layer.layer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 5, 17)) + + getStratum(id: string): { layer: string }; +>getStratum : Symbol(Layer.getStratum, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 6, 27)) +>id : Symbol(id, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 7, 15)) +>layer : Symbol(layer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 7, 29)) +} + +function hasOpacity(x: any): x is Opacity { +>hasOpacity : Symbol(hasOpacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 8, 1)) +>x : Symbol(x, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 10, 20)) +>x : Symbol(x, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 10, 20)) +>Opacity : Symbol(Opacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 0, 0)) + + return 'opacity' in x; +>x : Symbol(x, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 10, 20)) +} + +function hasLayer(x: any): x is Layer { +>hasLayer : Symbol(hasLayer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 12, 1)) +>x : Symbol(x, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 14, 18)) +>x : Symbol(x, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 14, 18)) +>Layer : Symbol(Layer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 3, 1)) + + return 'layer' in x; +>x : Symbol(x, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 14, 18)) +} + +const foo = {}; +>foo : Symbol(foo, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 18, 5)) + +if (hasOpacity(foo) && hasLayer(foo)) { +>hasOpacity : Symbol(hasOpacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 8, 1)) +>foo : Symbol(foo, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 18, 5)) +>hasLayer : Symbol(hasLayer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 12, 1)) +>foo : Symbol(foo, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 18, 5)) + + foo.getStratum('user').opacity = 0.5; +>foo.getStratum('user').opacity : Symbol(opacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 2, 29)) +>foo.getStratum : Symbol(getStratum, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 1, 29), Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 6, 27)) +>foo : Symbol(foo, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 18, 5)) +>getStratum : Symbol(getStratum, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 1, 29), Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 6, 27)) +>opacity : Symbol(opacity, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 2, 29)) + + foo.getStratum('user').layer = 'test'; +>foo.getStratum('user').layer : Symbol(layer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 7, 29)) +>foo.getStratum : Symbol(getStratum, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 1, 29), Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 6, 27)) +>foo : Symbol(foo, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 18, 5)) +>getStratum : Symbol(getStratum, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 1, 29), Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 6, 27)) +>layer : Symbol(layer, Decl(twoTypeGuardsAllowUseOfFunctionReturnType.ts, 7, 29)) +} + diff --git a/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.types b/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.types new file mode 100644 index 0000000000000..7c383be8d5f22 --- /dev/null +++ b/tests/baselines/reference/twoTypeGuardsAllowUseOfFunctionReturnType.types @@ -0,0 +1,77 @@ +=== tests/cases/compiler/twoTypeGuardsAllowUseOfFunctionReturnType.ts === +interface Opacity { + readonly opacity: number; +>opacity : number + + getStratum(id: string): { opacity: number }; +>getStratum : (id: string) => { opacity: number; } +>id : string +>opacity : number +} + +interface Layer { + readonly layer: string; +>layer : string + + getStratum(id: string): { layer: string }; +>getStratum : (id: string) => { layer: string; } +>id : string +>layer : string +} + +function hasOpacity(x: any): x is Opacity { +>hasOpacity : (x: any) => x is Opacity +>x : any + + return 'opacity' in x; +>'opacity' in x : boolean +>'opacity' : "opacity" +>x : any +} + +function hasLayer(x: any): x is Layer { +>hasLayer : (x: any) => x is Layer +>x : any + + return 'layer' in x; +>'layer' in x : boolean +>'layer' : "layer" +>x : any +} + +const foo = {}; +>foo : {} +>{} : {} + +if (hasOpacity(foo) && hasLayer(foo)) { +>hasOpacity(foo) && hasLayer(foo) : boolean +>hasOpacity(foo) : boolean +>hasOpacity : (x: any) => x is Opacity +>foo : {} +>hasLayer(foo) : boolean +>hasLayer : (x: any) => x is Layer +>foo : Opacity + + foo.getStratum('user').opacity = 0.5; +>foo.getStratum('user').opacity = 0.5 : 0.5 +>foo.getStratum('user').opacity : number +>foo.getStratum('user') : { opacity: number; } & { layer: string; } +>foo.getStratum : ((id: string) => { opacity: number; }) & ((id: string) => { layer: string; }) +>foo : Opacity & Layer +>getStratum : ((id: string) => { opacity: number; }) & ((id: string) => { layer: string; }) +>'user' : "user" +>opacity : number +>0.5 : 0.5 + + foo.getStratum('user').layer = 'test'; +>foo.getStratum('user').layer = 'test' : "test" +>foo.getStratum('user').layer : string +>foo.getStratum('user') : { opacity: number; } & { layer: string; } +>foo.getStratum : ((id: string) => { opacity: number; }) & ((id: string) => { layer: string; }) +>foo : Opacity & Layer +>getStratum : ((id: string) => { opacity: number; }) & ((id: string) => { layer: string; }) +>'user' : "user" +>layer : string +>'test' : "test" +} + diff --git a/tests/cases/compiler/intersectionOfCallsWithSameParameters.ts b/tests/cases/compiler/intersectionOfCallsWithSameParameters.ts index aad6b38438708..95aa2628e9187 100644 --- a/tests/cases/compiler/intersectionOfCallsWithSameParameters.ts +++ b/tests/cases/compiler/intersectionOfCallsWithSameParameters.ts @@ -1,38 +1,43 @@ // @strict: true interface One { - overload(id: string): { one: number }; - intersect(id: string): { one: number }; + differentParameterType(id: string): { one: number }; + differentNumberOfParameters(id: string): { one: number }; + differentTypeParameterDefault(id: string): { one: number }; + differentTypeParameterConstraint(id: string): { one: number }; + + same1(id: string): { one: number }; + same2(id: string): { one: number }; + same3(id: string): { one: number }; + same4(id: string): { one: number }; + same5(id: string): { one: number }; } interface Two { - overload(id: number): { two: number }; - intersect(id: string): { two: number }; + differentParameterType(id: number): { two: number }; + differentNumberOfParameters(id: string, second: string): { two: number }; + differentTypeParameterDefault(id: string): { two: number }; + differentTypeParameterConstraint(id: string): { two: number }; + + same1(id: string): { two: number }; + same2(id: string): { two: number }; + same3(id: string): { two: number }; + same4(id: string): { two: number }; + same5(id: string): { two: number }; } -class Both implements One, Two { - overload(id: number): { two: number }; - overload(id: string): { one: number }; - overload(id: string | number): { one: number, two: number } { - return { - one: 1, - two: 2 - }; - } +const i: One & Two = {}; - intersect(id: string): { one: number, two: number } { - return { - one: 1, - two: 2 - }; - } -} +// These lines should type check; the return type should be intersected. +const same1: { one: number, two: number } = i.same1('test'); +const same2: { one: number, two: number } = i.same2('test'); +const same3: { one: number, two: number } = i.same3<{ one:number }>('test'); +const same4: { one: number, two: number } = i.same4('test'); +const same5: { one: number, two: number } = i.same5<{ one:number }, string>('test'); -const b = new Both(); -const intersect: { one: number, two: number } = b.intersect('test'); -const overloadA: { one: number } = b.overload('test'); -const overloadB: { two: number } = b.overload(4); -const bAs: One & Two = b; -const asIntersect: { one: number, two: number } = bAs.intersect('test'); -const asOverloadA: { one: number } = bAs.overload('test'); -const asOverloadB: { two: number } = bAs.overload(4); +// These lines should not, because the functions should become overloads rather +// than the return types intersected. +const differentParameterType: { one: number, two: number } = i.differentParameterType('test'); +const differentNumberOfParameters: { one: number, two: number } = i.differentNumberOfParameters('test'); +const differentTypeParameterDefault: { one: number, two: number } = i.differentTypeParameterDefault('test'); +const differentTypeParameterConstraint: { one: number, two: number } = i.differentTypeParameterConstraint<{ one: number }>('test'); diff --git a/tests/cases/compiler/twoTypeGuardsAllowUseOfFunctionReturnType.ts b/tests/cases/compiler/twoTypeGuardsAllowUseOfFunctionReturnType.ts new file mode 100644 index 0000000000000..b3c0156008cd3 --- /dev/null +++ b/tests/cases/compiler/twoTypeGuardsAllowUseOfFunctionReturnType.ts @@ -0,0 +1,23 @@ +interface Opacity { + readonly opacity: number; + getStratum(id: string): { opacity: number }; +} + +interface Layer { + readonly layer: string; + getStratum(id: string): { layer: string }; +} + +function hasOpacity(x: any): x is Opacity { + return 'opacity' in x; +} + +function hasLayer(x: any): x is Layer { + return 'layer' in x; +} + +const foo = {}; +if (hasOpacity(foo) && hasLayer(foo)) { + foo.getStratum('user').opacity = 0.5; + foo.getStratum('user').layer = 'test'; +}