From 8ae7c6c227a009c977344abe655ecac39ceb14e2 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 9 Aug 2017 15:43:47 -0700 Subject: [PATCH 1/2] Fix type predicate index in error reporting Previously type predicate error checking didn't take the `this` parameter into account when checking for errors. This led to spurious errors. Note that it's not feasible to change the initial value of `parameterIndex` because several checks refer to the original declaration, for example to make sure that a type predicate doesn't refer to a rest parameter. --- src/compiler/checker.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 46cb8bb4937..aaf5b0bc4f9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8592,7 +8592,7 @@ namespace ts { // The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions if (target.typePredicate) { if (source.typePredicate) { - result &= compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, reportErrors, errorReporter, compareTypes); + result &= compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, source.declaration, target.declaration, reportErrors, errorReporter, compareTypes); } else if (isIdentifierTypePredicate(target.typePredicate)) { if (reportErrors) { @@ -8614,8 +8614,11 @@ namespace ts { return result; } - function compareTypePredicateRelatedTo(source: TypePredicate, + function compareTypePredicateRelatedTo( + source: TypePredicate, target: TypePredicate, + sourceDeclaration: SignatureDeclaration, + targetDeclaration: SignatureDeclaration, reportErrors: boolean, errorReporter: ErrorReporter, compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary { @@ -8628,11 +8631,13 @@ namespace ts { } if (source.kind === TypePredicateKind.Identifier) { - const sourceIdentifierPredicate = source as IdentifierTypePredicate; - const targetIdentifierPredicate = target as IdentifierTypePredicate; - if (sourceIdentifierPredicate.parameterIndex !== targetIdentifierPredicate.parameterIndex) { + const sourcePredicate = source as IdentifierTypePredicate; + const targetPredicate = target as IdentifierTypePredicate; + const sourceIndex = sourcePredicate.parameterIndex - (getThisParameter(sourceDeclaration) ? 1 : 0); + const targetIndex = targetPredicate.parameterIndex - (getThisParameter(targetDeclaration) ? 1 : 0); + if (sourceIndex !== targetIndex) { if (reportErrors) { - errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourceIdentifierPredicate.parameterName, targetIdentifierPredicate.parameterName); + errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourcePredicate.parameterName, targetPredicate.parameterName); errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target)); } return Ternary.False; From a59f77ffb41efa059ece19665147e562841b987f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 9 Aug 2017 15:45:28 -0700 Subject: [PATCH 2/2] Test:type predicate uses correct index to report errors --- .../reference/thisTypeInTypePredicate.js | 7 ++++++ .../reference/thisTypeInTypePredicate.symbols | 18 +++++++++++++++ .../reference/thisTypeInTypePredicate.types | 23 +++++++++++++++++++ .../types/thisType/thisTypeInTypePredicate.ts | 2 ++ 4 files changed, 50 insertions(+) create mode 100644 tests/baselines/reference/thisTypeInTypePredicate.js create mode 100644 tests/baselines/reference/thisTypeInTypePredicate.symbols create mode 100644 tests/baselines/reference/thisTypeInTypePredicate.types create mode 100644 tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts diff --git a/tests/baselines/reference/thisTypeInTypePredicate.js b/tests/baselines/reference/thisTypeInTypePredicate.js new file mode 100644 index 00000000000..1b9a48daeea --- /dev/null +++ b/tests/baselines/reference/thisTypeInTypePredicate.js @@ -0,0 +1,7 @@ +//// [thisTypeInTypePredicate.ts] +declare function filter(f: (this: void, x: any) => x is S): S[]; +const numbers = filter((x): x is number => 'number' == typeof x) + + +//// [thisTypeInTypePredicate.js] +var numbers = filter(function (x) { return 'number' == typeof x; }); diff --git a/tests/baselines/reference/thisTypeInTypePredicate.symbols b/tests/baselines/reference/thisTypeInTypePredicate.symbols new file mode 100644 index 00000000000..ff18f450a0e --- /dev/null +++ b/tests/baselines/reference/thisTypeInTypePredicate.symbols @@ -0,0 +1,18 @@ +=== tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts === +declare function filter(f: (this: void, x: any) => x is S): S[]; +>filter : Symbol(filter, Decl(thisTypeInTypePredicate.ts, 0, 0)) +>S : Symbol(S, Decl(thisTypeInTypePredicate.ts, 0, 24)) +>f : Symbol(f, Decl(thisTypeInTypePredicate.ts, 0, 27)) +>this : Symbol(this, Decl(thisTypeInTypePredicate.ts, 0, 31)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 0, 42)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 0, 42)) +>S : Symbol(S, Decl(thisTypeInTypePredicate.ts, 0, 24)) +>S : Symbol(S, Decl(thisTypeInTypePredicate.ts, 0, 24)) + +const numbers = filter((x): x is number => 'number' == typeof x) +>numbers : Symbol(numbers, Decl(thisTypeInTypePredicate.ts, 1, 5)) +>filter : Symbol(filter, Decl(thisTypeInTypePredicate.ts, 0, 0)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 1, 32)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 1, 32)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 1, 32)) + diff --git a/tests/baselines/reference/thisTypeInTypePredicate.types b/tests/baselines/reference/thisTypeInTypePredicate.types new file mode 100644 index 00000000000..cc92ce811b8 --- /dev/null +++ b/tests/baselines/reference/thisTypeInTypePredicate.types @@ -0,0 +1,23 @@ +=== tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts === +declare function filter(f: (this: void, x: any) => x is S): S[]; +>filter : (f: (this: void, x: any) => x is S) => S[] +>S : S +>f : (this: void, x: any) => x is S +>this : void +>x : any +>x : any +>S : S +>S : S + +const numbers = filter((x): x is number => 'number' == typeof x) +>numbers : number[] +>filter((x): x is number => 'number' == typeof x) : number[] +>filter : (f: (this: void, x: any) => x is S) => S[] +>(x): x is number => 'number' == typeof x : (this: void, x: any) => x is number +>x : any +>x : any +>'number' == typeof x : boolean +>'number' : "number" +>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : any + diff --git a/tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts b/tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts new file mode 100644 index 00000000000..ed0ebc75a22 --- /dev/null +++ b/tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts @@ -0,0 +1,2 @@ +declare function filter(f: (this: void, x: any) => x is S): S[]; +const numbers = filter((x): x is number => 'number' == typeof x)