From c5b47fb5c8ce348a4cec1cd52654995052c20cc2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 29 Oct 2015 14:53:14 -0700 Subject: [PATCH 1/3] Correct partial signature matching --- src/compiler/checker.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 99b3d97b7be..1fde2711e12 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5603,18 +5603,29 @@ namespace ts { return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp)); } + // A source signature matches a target signature if the two signatures have the same number of required, + // optional, and rest parameters. + function isMatchingSignature(source: Signature, target: Signature) { + return source.parameters.length === target.parameters.length && + source.minArgumentCount === target.minArgumentCount && + source.hasRestParameter === target.hasRestParameter; + } + + // A source signature partially matches a target signature if the target signature has no fewer required + // parameters and no more overall parameters than the source signature (where a signature with a rest + // parameter is always considered to have more overall parameters than one without). + function isPartiallyMatchingSignature(source: Signature, target: Signature) { + return source.minArgumentCount <= target.minArgumentCount && ( + source.hasRestParameter && !target.hasRestParameter || + source.hasRestParameter === target.hasRestParameter && source.parameters.length >= target.parameters.length); + } + function compareSignatures(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary { if (source === target) { return Ternary.True; } - if (source.parameters.length !== target.parameters.length || - source.minArgumentCount !== target.minArgumentCount || - source.hasRestParameter !== target.hasRestParameter) { - if (!partialMatch || - source.parameters.length < target.parameters.length && !source.hasRestParameter || - source.minArgumentCount > target.minArgumentCount) { - return Ternary.False; - } + if (!(isMatchingSignature(source, target) || partialMatch && isPartiallyMatchingSignature(source, target))) { + return Ternary.False; } let result = Ternary.True; if (source.typeParameters && target.typeParameters) { From a27ed01516873d2ac0c70b32135eddf30461eba7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 29 Oct 2015 14:53:28 -0700 Subject: [PATCH 2/3] Adding test --- .../unionTypeCallSignatures4.errors.txt | 41 +++++++++++++++++ .../reference/unionTypeCallSignatures4.js | 45 +++++++++++++++++++ .../types/union/unionTypeCallSignatures4.ts | 25 +++++++++++ 3 files changed, 111 insertions(+) create mode 100644 tests/baselines/reference/unionTypeCallSignatures4.errors.txt create mode 100644 tests/baselines/reference/unionTypeCallSignatures4.js create mode 100644 tests/cases/conformance/types/union/unionTypeCallSignatures4.ts diff --git a/tests/baselines/reference/unionTypeCallSignatures4.errors.txt b/tests/baselines/reference/unionTypeCallSignatures4.errors.txt new file mode 100644 index 00000000000..585906a6b5a --- /dev/null +++ b/tests/baselines/reference/unionTypeCallSignatures4.errors.txt @@ -0,0 +1,41 @@ +tests/cases/conformance/types/union/unionTypeCallSignatures4.ts(10,1): error TS2346: Supplied parameters do not match any signature of call target. +tests/cases/conformance/types/union/unionTypeCallSignatures4.ts(20,1): error TS2346: Supplied parameters do not match any signature of call target. +tests/cases/conformance/types/union/unionTypeCallSignatures4.ts(23,1): error TS2346: Supplied parameters do not match any signature of call target. +tests/cases/conformance/types/union/unionTypeCallSignatures4.ts(25,1): error TS2346: Supplied parameters do not match any signature of call target. + + +==== tests/cases/conformance/types/union/unionTypeCallSignatures4.ts (4 errors) ==== + type F1 = (a: string, b?: string) => void; + type F2 = (a: string, b?: string, c?: string) => void; + type F3 = (a: string, ...rest: string[]) => void; + type F4 = (a: string, b?: string, ...rest: string[]) => void; + type F5 = (a: string, b: string) => void; + + var f12: F1 | F2; + f12("a"); + f12("a", "b"); + f12("a", "b", "c"); // error + ~~~~~~~~~~~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + + var f34: F3 | F4; + f34("a"); + f34("a", "b"); + f34("a", "b", "c"); + + var f1234: F1 | F2 | F3 | F4; + f1234("a"); + f1234("a", "b"); + f1234("a", "b", "c"); // error + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + + var f12345: F1 | F2 | F3 | F4 | F5; + f12345("a"); // error + ~~~~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + f12345("a", "b"); + f12345("a", "b", "c"); // error + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + \ No newline at end of file diff --git a/tests/baselines/reference/unionTypeCallSignatures4.js b/tests/baselines/reference/unionTypeCallSignatures4.js new file mode 100644 index 00000000000..be9a45b33c9 --- /dev/null +++ b/tests/baselines/reference/unionTypeCallSignatures4.js @@ -0,0 +1,45 @@ +//// [unionTypeCallSignatures4.ts] +type F1 = (a: string, b?: string) => void; +type F2 = (a: string, b?: string, c?: string) => void; +type F3 = (a: string, ...rest: string[]) => void; +type F4 = (a: string, b?: string, ...rest: string[]) => void; +type F5 = (a: string, b: string) => void; + +var f12: F1 | F2; +f12("a"); +f12("a", "b"); +f12("a", "b", "c"); // error + +var f34: F3 | F4; +f34("a"); +f34("a", "b"); +f34("a", "b", "c"); + +var f1234: F1 | F2 | F3 | F4; +f1234("a"); +f1234("a", "b"); +f1234("a", "b", "c"); // error + +var f12345: F1 | F2 | F3 | F4 | F5; +f12345("a"); // error +f12345("a", "b"); +f12345("a", "b", "c"); // error + + +//// [unionTypeCallSignatures4.js] +var f12; +f12("a"); +f12("a", "b"); +f12("a", "b", "c"); // error +var f34; +f34("a"); +f34("a", "b"); +f34("a", "b", "c"); +var f1234; +f1234("a"); +f1234("a", "b"); +f1234("a", "b", "c"); // error +var f12345; +f12345("a"); // error +f12345("a", "b"); +f12345("a", "b", "c"); // error diff --git a/tests/cases/conformance/types/union/unionTypeCallSignatures4.ts b/tests/cases/conformance/types/union/unionTypeCallSignatures4.ts new file mode 100644 index 00000000000..1e27acd2083 --- /dev/null +++ b/tests/cases/conformance/types/union/unionTypeCallSignatures4.ts @@ -0,0 +1,25 @@ +type F1 = (a: string, b?: string) => void; +type F2 = (a: string, b?: string, c?: string) => void; +type F3 = (a: string, ...rest: string[]) => void; +type F4 = (a: string, b?: string, ...rest: string[]) => void; +type F5 = (a: string, b: string) => void; + +var f12: F1 | F2; +f12("a"); +f12("a", "b"); +f12("a", "b", "c"); // error + +var f34: F3 | F4; +f34("a"); +f34("a", "b"); +f34("a", "b", "c"); + +var f1234: F1 | F2 | F3 | F4; +f1234("a"); +f1234("a", "b"); +f1234("a", "b", "c"); // error + +var f12345: F1 | F2 | F3 | F4 | F5; +f12345("a"); // error +f12345("a", "b"); +f12345("a", "b", "c"); // error From 31331ff6d1399acf51ec68edef0d6052a468b255 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 5 Nov 2015 16:31:30 -0800 Subject: [PATCH 3/3] Addressing CR feedback --- src/compiler/checker.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1fde2711e12..836c16b51d8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5603,28 +5603,30 @@ namespace ts { return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp)); } - // A source signature matches a target signature if the two signatures have the same number of required, - // optional, and rest parameters. - function isMatchingSignature(source: Signature, target: Signature) { - return source.parameters.length === target.parameters.length && + function isMatchingSignature(source: Signature, target: Signature, partialMatch: boolean) { + // A source signature matches a target signature if the two signatures have the same number of required, + // optional, and rest parameters. + if (source.parameters.length === target.parameters.length && source.minArgumentCount === target.minArgumentCount && - source.hasRestParameter === target.hasRestParameter; - } - - // A source signature partially matches a target signature if the target signature has no fewer required - // parameters and no more overall parameters than the source signature (where a signature with a rest - // parameter is always considered to have more overall parameters than one without). - function isPartiallyMatchingSignature(source: Signature, target: Signature) { - return source.minArgumentCount <= target.minArgumentCount && ( + source.hasRestParameter === target.hasRestParameter) { + return true; + } + // A source signature partially matches a target signature if the target signature has no fewer required + // parameters and no more overall parameters than the source signature (where a signature with a rest + // parameter is always considered to have more overall parameters than one without). + if (partialMatch && source.minArgumentCount <= target.minArgumentCount && ( source.hasRestParameter && !target.hasRestParameter || - source.hasRestParameter === target.hasRestParameter && source.parameters.length >= target.parameters.length); + source.hasRestParameter === target.hasRestParameter && source.parameters.length >= target.parameters.length)) { + return true; + } + return false; } function compareSignatures(source: Signature, target: Signature, partialMatch: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary { if (source === target) { return Ternary.True; } - if (!(isMatchingSignature(source, target) || partialMatch && isPartiallyMatchingSignature(source, target))) { + if (!(isMatchingSignature(source, target, partialMatch))) { return Ternary.False; } let result = Ternary.True;