diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d43bd907c4635..57e9d02ddd8cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11565,7 +11565,7 @@ namespace ts { } if (isIdentifierTypePredicate(predicate)) { - const predicateArgument = callExpression.arguments[predicate.parameterIndex]; + const predicateArgument = callExpression.arguments[predicate.parameterIndex - (signature.thisParameter ? 1 : 0)]; if (predicateArgument) { if (isMatchingReference(reference, predicateArgument)) { return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf); diff --git a/tests/baselines/reference/typePredicateWithThisParameter.js b/tests/baselines/reference/typePredicateWithThisParameter.js new file mode 100644 index 0000000000000..38d84546372bd --- /dev/null +++ b/tests/baselines/reference/typePredicateWithThisParameter.js @@ -0,0 +1,43 @@ +//// [typePredicateWithThisParameter.ts] +// Repro from #15310 + +interface Foo { + foo: string; +} +interface Bar { + bar: string; +} + +function isFoo1(object: {}): object is Foo { + return 'foo' in object; +} + +function isFoo2(this: void, object: {}): object is Foo { + return 'foo' in object; +} + +declare let test: Foo | Bar; + +if (isFoo1(test)) { + test.foo = 'hi'; +} + +if (isFoo2(test)) { + test.foo = 'hi'; +} + + +//// [typePredicateWithThisParameter.js] +// Repro from #15310 +function isFoo1(object) { + return 'foo' in object; +} +function isFoo2(object) { + return 'foo' in object; +} +if (isFoo1(test)) { + test.foo = 'hi'; +} +if (isFoo2(test)) { + test.foo = 'hi'; +} diff --git a/tests/baselines/reference/typePredicateWithThisParameter.symbols b/tests/baselines/reference/typePredicateWithThisParameter.symbols new file mode 100644 index 0000000000000..e0de3073051bc --- /dev/null +++ b/tests/baselines/reference/typePredicateWithThisParameter.symbols @@ -0,0 +1,62 @@ +=== tests/cases/compiler/typePredicateWithThisParameter.ts === +// Repro from #15310 + +interface Foo { +>Foo : Symbol(Foo, Decl(typePredicateWithThisParameter.ts, 0, 0)) + + foo: string; +>foo : Symbol(Foo.foo, Decl(typePredicateWithThisParameter.ts, 2, 15)) +} +interface Bar { +>Bar : Symbol(Bar, Decl(typePredicateWithThisParameter.ts, 4, 1)) + + bar: string; +>bar : Symbol(Bar.bar, Decl(typePredicateWithThisParameter.ts, 5, 15)) +} + +function isFoo1(object: {}): object is Foo { +>isFoo1 : Symbol(isFoo1, Decl(typePredicateWithThisParameter.ts, 7, 1)) +>object : Symbol(object, Decl(typePredicateWithThisParameter.ts, 9, 16)) +>object : Symbol(object, Decl(typePredicateWithThisParameter.ts, 9, 16)) +>Foo : Symbol(Foo, Decl(typePredicateWithThisParameter.ts, 0, 0)) + + return 'foo' in object; +>object : Symbol(object, Decl(typePredicateWithThisParameter.ts, 9, 16)) +} + +function isFoo2(this: void, object: {}): object is Foo { +>isFoo2 : Symbol(isFoo2, Decl(typePredicateWithThisParameter.ts, 11, 1)) +>this : Symbol(this, Decl(typePredicateWithThisParameter.ts, 13, 16)) +>object : Symbol(object, Decl(typePredicateWithThisParameter.ts, 13, 27)) +>object : Symbol(object, Decl(typePredicateWithThisParameter.ts, 13, 27)) +>Foo : Symbol(Foo, Decl(typePredicateWithThisParameter.ts, 0, 0)) + + return 'foo' in object; +>object : Symbol(object, Decl(typePredicateWithThisParameter.ts, 13, 27)) +} + +declare let test: Foo | Bar; +>test : Symbol(test, Decl(typePredicateWithThisParameter.ts, 17, 11)) +>Foo : Symbol(Foo, Decl(typePredicateWithThisParameter.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(typePredicateWithThisParameter.ts, 4, 1)) + +if (isFoo1(test)) { +>isFoo1 : Symbol(isFoo1, Decl(typePredicateWithThisParameter.ts, 7, 1)) +>test : Symbol(test, Decl(typePredicateWithThisParameter.ts, 17, 11)) + + test.foo = 'hi'; +>test.foo : Symbol(Foo.foo, Decl(typePredicateWithThisParameter.ts, 2, 15)) +>test : Symbol(test, Decl(typePredicateWithThisParameter.ts, 17, 11)) +>foo : Symbol(Foo.foo, Decl(typePredicateWithThisParameter.ts, 2, 15)) +} + +if (isFoo2(test)) { +>isFoo2 : Symbol(isFoo2, Decl(typePredicateWithThisParameter.ts, 11, 1)) +>test : Symbol(test, Decl(typePredicateWithThisParameter.ts, 17, 11)) + + test.foo = 'hi'; +>test.foo : Symbol(Foo.foo, Decl(typePredicateWithThisParameter.ts, 2, 15)) +>test : Symbol(test, Decl(typePredicateWithThisParameter.ts, 17, 11)) +>foo : Symbol(Foo.foo, Decl(typePredicateWithThisParameter.ts, 2, 15)) +} + diff --git a/tests/baselines/reference/typePredicateWithThisParameter.types b/tests/baselines/reference/typePredicateWithThisParameter.types new file mode 100644 index 0000000000000..382a12faa9ddd --- /dev/null +++ b/tests/baselines/reference/typePredicateWithThisParameter.types @@ -0,0 +1,72 @@ +=== tests/cases/compiler/typePredicateWithThisParameter.ts === +// Repro from #15310 + +interface Foo { +>Foo : Foo + + foo: string; +>foo : string +} +interface Bar { +>Bar : Bar + + bar: string; +>bar : string +} + +function isFoo1(object: {}): object is Foo { +>isFoo1 : (object: {}) => object is Foo +>object : {} +>object : any +>Foo : Foo + + return 'foo' in object; +>'foo' in object : boolean +>'foo' : "foo" +>object : {} +} + +function isFoo2(this: void, object: {}): object is Foo { +>isFoo2 : (this: void, object: {}) => object is Foo +>this : void +>object : {} +>object : any +>Foo : Foo + + return 'foo' in object; +>'foo' in object : boolean +>'foo' : "foo" +>object : {} +} + +declare let test: Foo | Bar; +>test : Foo | Bar +>Foo : Foo +>Bar : Bar + +if (isFoo1(test)) { +>isFoo1(test) : boolean +>isFoo1 : (object: {}) => object is Foo +>test : Foo | Bar + + test.foo = 'hi'; +>test.foo = 'hi' : "hi" +>test.foo : string +>test : Foo +>foo : string +>'hi' : "hi" +} + +if (isFoo2(test)) { +>isFoo2(test) : boolean +>isFoo2 : (this: void, object: {}) => object is Foo +>test : Foo | Bar + + test.foo = 'hi'; +>test.foo = 'hi' : "hi" +>test.foo : string +>test : Foo +>foo : string +>'hi' : "hi" +} + diff --git a/tests/cases/compiler/typePredicateWithThisParameter.ts b/tests/cases/compiler/typePredicateWithThisParameter.ts new file mode 100644 index 0000000000000..de2b6eb94c8a3 --- /dev/null +++ b/tests/cases/compiler/typePredicateWithThisParameter.ts @@ -0,0 +1,26 @@ +// Repro from #15310 + +interface Foo { + foo: string; +} +interface Bar { + bar: string; +} + +function isFoo1(object: {}): object is Foo { + return 'foo' in object; +} + +function isFoo2(this: void, object: {}): object is Foo { + return 'foo' in object; +} + +declare let test: Foo | Bar; + +if (isFoo1(test)) { + test.foo = 'hi'; +} + +if (isFoo2(test)) { + test.foo = 'hi'; +}