diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8b1477961ab2d..9e41204136678 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -736,6 +736,7 @@ import { isThisInTypeQuery, isThisProperty, isThisTypeParameter, + isThisTypePredicate, isTransientSymbol, isTupleTypeNode, isTypeAlias, @@ -21217,7 +21218,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (sourceTypePredicate) { result &= compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes); } - else if (isIdentifierTypePredicate(targetTypePredicate)) { + else if (isIdentifierTypePredicate(targetTypePredicate) || isThisTypePredicate(targetTypePredicate)) { if (reportErrors) { errorReporter!(Diagnostics.Signature_0_must_be_a_type_predicate, signatureToString(source)); } diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index e06c943aed1c5..ecf62c7445f91 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -591,7 +591,7 @@ export class LineNode implements LineCollection { if (children.length) this.updateCounts(); } - isLeaf() { + isLeaf(): this is LineLeaf { return false; } @@ -839,7 +839,7 @@ export class LineLeaf implements LineCollection { constructor(public text: string) { } - isLeaf() { + isLeaf(): this is LineLeaf { return true; } diff --git a/tests/baselines/reference/implementArrayInterface.errors.txt b/tests/baselines/reference/implementArrayInterface.errors.txt new file mode 100644 index 0000000000000..4d6b297e6093a --- /dev/null +++ b/tests/baselines/reference/implementArrayInterface.errors.txt @@ -0,0 +1,43 @@ +implementArrayInterface.ts(19,5): error TS2416: Property 'every' in type 'MyArray' is not assignable to the same property in base type 'T[]'. + Type '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => boolean' is not assignable to type '{ (predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean; }'. + Signature '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean' must be a type predicate. + + +==== implementArrayInterface.ts (1 errors) ==== + declare class MyArray implements Array { + toString(): string; + toLocaleString(): string; + concat(...items: U[]): T[]; + concat(...items: T[]): T[]; + join(separator?: string): string; + pop(): T; + push(...items: T[]): number; + reverse(): T[]; + shift(): T; + slice(start?: number, end?: number): T[]; + sort(compareFn?: (a: T, b: T) => number): this; + splice(start: number): T[]; + splice(start: number, deleteCount: number, ...items: T[]): T[]; + unshift(...items: T[]): number; + + indexOf(searchElement: T, fromIndex?: number): number; + lastIndexOf(searchElement: T, fromIndex?: number): number; + every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + ~~~~~ +!!! error TS2416: Property 'every' in type 'MyArray' is not assignable to the same property in base type 'T[]'. +!!! error TS2416: Type '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => boolean' is not assignable to type '{ (predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean; }'. +!!! error TS2416: Signature '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean' must be a type predicate. + some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; + map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; + filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + + length: number; + + [n: number]: T; + } + \ No newline at end of file diff --git a/tests/baselines/reference/implementArrayInterface.types b/tests/baselines/reference/implementArrayInterface.types index b3afd45a48a6f..8dfcb2f054d76 100644 --- a/tests/baselines/reference/implementArrayInterface.types +++ b/tests/baselines/reference/implementArrayInterface.types @@ -117,6 +117,7 @@ declare class MyArray implements Array { >array : T[] > : ^^^ >thisArg : any +> : ^^^ some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; >some : (callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => boolean @@ -130,6 +131,7 @@ declare class MyArray implements Array { >array : T[] > : ^^^ >thisArg : any +> : ^^^ forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; >forEach : (callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any) => void @@ -143,6 +145,7 @@ declare class MyArray implements Array { >array : T[] > : ^^^ >thisArg : any +> : ^^^ map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; >map : (callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[] @@ -156,6 +159,7 @@ declare class MyArray implements Array { >array : T[] > : ^^^ >thisArg : any +> : ^^^ filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; >filter : (callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => T[] @@ -169,6 +173,7 @@ declare class MyArray implements Array { >array : T[] > : ^^^ >thisArg : any +> : ^^^ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; >reduce : { (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; (callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; } diff --git a/tests/baselines/reference/typePredicateInherit.errors.txt b/tests/baselines/reference/typePredicateInherit.errors.txt new file mode 100644 index 0000000000000..c29eaef9616f8 --- /dev/null +++ b/tests/baselines/reference/typePredicateInherit.errors.txt @@ -0,0 +1,91 @@ +typePredicateInherit.ts(11,3): error TS2416: Property 'method1' in type 'B' is not assignable to the same property in base type 'A'. + Type '() => void' is not assignable to type '() => this is { a: 1; }'. + Signature '(): void' must be a type predicate. +typePredicateInherit.ts(13,3): error TS2416: Property 'method2' in type 'B' is not assignable to the same property in base type 'A'. + Type '() => void' is not assignable to type '() => boolean'. + Type 'void' is not assignable to type 'boolean'. +typePredicateInherit.ts(15,3): error TS2416: Property 'method3' in type 'B' is not assignable to the same property in base type 'A'. + Type '() => boolean' is not assignable to type '() => this is { a: 1; }'. + Signature '(): boolean' must be a type predicate. +typePredicateInherit.ts(41,3): error TS2416: Property 'method1' in type 'D' is not assignable to the same property in base type 'C'. + Type '() => void' is not assignable to type '() => this is { a: 1; }'. + Signature '(): void' must be a type predicate. +typePredicateInherit.ts(50,3): error TS2416: Property 'method3' in type 'D' is not assignable to the same property in base type 'C'. + Type '() => boolean' is not assignable to type '() => this is { a: 1; }'. + Signature '(): boolean' must be a type predicate. + + +==== typePredicateInherit.ts (5 errors) ==== + interface A { + method1(): this is { + a: 1 + } + method2(): boolean; + method3(): this is { + a: 1 + }; + } + class B implements A { + method1() { } // should error + ~~~~~~~ +!!! error TS2416: Property 'method1' in type 'B' is not assignable to the same property in base type 'A'. +!!! error TS2416: Type '() => void' is not assignable to type '() => this is { a: 1; }'. +!!! error TS2416: Signature '(): void' must be a type predicate. + + method2() { } // should error + ~~~~~~~ +!!! error TS2416: Property 'method2' in type 'B' is not assignable to the same property in base type 'A'. +!!! error TS2416: Type '() => void' is not assignable to type '() => boolean'. +!!! error TS2416: Type 'void' is not assignable to type 'boolean'. + + method3() { // should error + ~~~~~~~ +!!! error TS2416: Property 'method3' in type 'B' is not assignable to the same property in base type 'A'. +!!! error TS2416: Type '() => boolean' is not assignable to type '() => this is { a: 1; }'. +!!! error TS2416: Signature '(): boolean' must be a type predicate. + return true + } + } + + class C { + method1(): this is { + a: 1 + } { + return true; + } + + method2(): this is { + a: 1 + } { + return true; + } + + method3(): this is { + a: 1 + } { + return true; + } + } + + class D extends C { + method1(): void { // should error + ~~~~~~~ +!!! error TS2416: Property 'method1' in type 'D' is not assignable to the same property in base type 'C'. +!!! error TS2416: Type '() => void' is not assignable to type '() => this is { a: 1; }'. +!!! error TS2416: Signature '(): void' must be a type predicate. + } + + method2(): this is { // should ok + a: 1 + } { + return true; + } + + method3(): boolean { // should error + ~~~~~~~ +!!! error TS2416: Property 'method3' in type 'D' is not assignable to the same property in base type 'C'. +!!! error TS2416: Type '() => boolean' is not assignable to type '() => this is { a: 1; }'. +!!! error TS2416: Signature '(): boolean' must be a type predicate. + return true; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/typePredicateInherit.js b/tests/baselines/reference/typePredicateInherit.js new file mode 100644 index 0000000000000..140ab72902701 --- /dev/null +++ b/tests/baselines/reference/typePredicateInherit.js @@ -0,0 +1,112 @@ +//// [tests/cases/compiler/typePredicateInherit.ts] //// + +//// [typePredicateInherit.ts] +interface A { + method1(): this is { + a: 1 + } + method2(): boolean; + method3(): this is { + a: 1 + }; +} +class B implements A { + method1() { } // should error + + method2() { } // should error + + method3() { // should error + return true + } +} + +class C { + method1(): this is { + a: 1 + } { + return true; + } + + method2(): this is { + a: 1 + } { + return true; + } + + method3(): this is { + a: 1 + } { + return true; + } +} + +class D extends C { + method1(): void { // should error + } + + method2(): this is { // should ok + a: 1 + } { + return true; + } + + method3(): boolean { // should error + return true; + } +} + +//// [typePredicateInherit.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var B = /** @class */ (function () { + function B() { + } + B.prototype.method1 = function () { }; // should error + B.prototype.method2 = function () { }; // should error + B.prototype.method3 = function () { + return true; + }; + return B; +}()); +var C = /** @class */ (function () { + function C() { + } + C.prototype.method1 = function () { + return true; + }; + C.prototype.method2 = function () { + return true; + }; + C.prototype.method3 = function () { + return true; + }; + return C; +}()); +var D = /** @class */ (function (_super) { + __extends(D, _super); + function D() { + return _super !== null && _super.apply(this, arguments) || this; + } + D.prototype.method1 = function () { + }; + D.prototype.method2 = function () { + return true; + }; + D.prototype.method3 = function () { + return true; + }; + return D; +}(C)); diff --git a/tests/baselines/reference/typePredicateInherit.symbols b/tests/baselines/reference/typePredicateInherit.symbols new file mode 100644 index 0000000000000..00f31ea459c23 --- /dev/null +++ b/tests/baselines/reference/typePredicateInherit.symbols @@ -0,0 +1,98 @@ +//// [tests/cases/compiler/typePredicateInherit.ts] //// + +=== typePredicateInherit.ts === +interface A { +>A : Symbol(A, Decl(typePredicateInherit.ts, 0, 0)) + + method1(): this is { +>method1 : Symbol(A.method1, Decl(typePredicateInherit.ts, 0, 13)) + + a: 1 +>a : Symbol(a, Decl(typePredicateInherit.ts, 1, 22)) + } + method2(): boolean; +>method2 : Symbol(A.method2, Decl(typePredicateInherit.ts, 3, 3)) + + method3(): this is { +>method3 : Symbol(A.method3, Decl(typePredicateInherit.ts, 4, 21)) + + a: 1 +>a : Symbol(a, Decl(typePredicateInherit.ts, 5, 22)) + + }; +} +class B implements A { +>B : Symbol(B, Decl(typePredicateInherit.ts, 8, 1)) +>A : Symbol(A, Decl(typePredicateInherit.ts, 0, 0)) + + method1() { } // should error +>method1 : Symbol(B.method1, Decl(typePredicateInherit.ts, 9, 22)) + + method2() { } // should error +>method2 : Symbol(B.method2, Decl(typePredicateInherit.ts, 10, 15)) + + method3() { // should error +>method3 : Symbol(B.method3, Decl(typePredicateInherit.ts, 12, 15)) + + return true + } +} + +class C { +>C : Symbol(C, Decl(typePredicateInherit.ts, 17, 1)) + + method1(): this is { +>method1 : Symbol(C.method1, Decl(typePredicateInherit.ts, 19, 9)) + + a: 1 +>a : Symbol(a, Decl(typePredicateInherit.ts, 20, 22)) + + } { + return true; + } + + method2(): this is { +>method2 : Symbol(C.method2, Decl(typePredicateInherit.ts, 24, 3)) + + a: 1 +>a : Symbol(a, Decl(typePredicateInherit.ts, 26, 22)) + + } { + return true; + } + + method3(): this is { +>method3 : Symbol(C.method3, Decl(typePredicateInherit.ts, 30, 3)) + + a: 1 +>a : Symbol(a, Decl(typePredicateInherit.ts, 32, 22)) + + } { + return true; + } +} + +class D extends C { +>D : Symbol(D, Decl(typePredicateInherit.ts, 37, 1)) +>C : Symbol(C, Decl(typePredicateInherit.ts, 17, 1)) + + method1(): void { // should error +>method1 : Symbol(D.method1, Decl(typePredicateInherit.ts, 39, 19)) + } + + method2(): this is { // should ok +>method2 : Symbol(D.method2, Decl(typePredicateInherit.ts, 41, 3)) + + a: 1 +>a : Symbol(a, Decl(typePredicateInherit.ts, 43, 22)) + + } { + return true; + } + + method3(): boolean { // should error +>method3 : Symbol(D.method3, Decl(typePredicateInherit.ts, 47, 3)) + + return true; + } +} diff --git a/tests/baselines/reference/typePredicateInherit.types b/tests/baselines/reference/typePredicateInherit.types new file mode 100644 index 0000000000000..eb6a03190bfa4 --- /dev/null +++ b/tests/baselines/reference/typePredicateInherit.types @@ -0,0 +1,129 @@ +//// [tests/cases/compiler/typePredicateInherit.ts] //// + +=== typePredicateInherit.ts === +interface A { + method1(): this is { +>method1 : () => this is { a: 1; } +> : ^^^^^^ + + a: 1 +>a : 1 +> : ^ + } + method2(): boolean; +>method2 : () => boolean +> : ^^^^^^ + + method3(): this is { +>method3 : () => this is { a: 1; } +> : ^^^^^^ + + a: 1 +>a : 1 +> : ^ + + }; +} +class B implements A { +>B : B +> : ^ + + method1() { } // should error +>method1 : () => void +> : ^^^^^^^^^^ + + method2() { } // should error +>method2 : () => void +> : ^^^^^^^^^^ + + method3() { // should error +>method3 : () => boolean +> : ^^^^^^^^^^^^^ + + return true +>true : true +> : ^^^^ + } +} + +class C { +>C : C +> : ^ + + method1(): this is { +>method1 : () => this is { a: 1; } +> : ^^^^^^ + + a: 1 +>a : 1 +> : ^ + + } { + return true; +>true : true +> : ^^^^ + } + + method2(): this is { +>method2 : () => this is { a: 1; } +> : ^^^^^^ + + a: 1 +>a : 1 +> : ^ + + } { + return true; +>true : true +> : ^^^^ + } + + method3(): this is { +>method3 : () => this is { a: 1; } +> : ^^^^^^ + + a: 1 +>a : 1 +> : ^ + + } { + return true; +>true : true +> : ^^^^ + } +} + +class D extends C { +>D : D +> : ^ +>C : C +> : ^ + + method1(): void { // should error +>method1 : () => void +> : ^^^^^^ + } + + method2(): this is { // should ok +>method2 : () => this is { a: 1; } +> : ^^^^^^ + + a: 1 +>a : 1 +> : ^ + + } { + return true; +>true : true +> : ^^^^ + } + + method3(): boolean { // should error +>method3 : () => boolean +> : ^^^^^^ + + return true; +>true : true +> : ^^^^ + } +} diff --git a/tests/cases/compiler/typePredicateInherit.ts b/tests/cases/compiler/typePredicateInherit.ts new file mode 100644 index 0000000000000..60d40463e414c --- /dev/null +++ b/tests/cases/compiler/typePredicateInherit.ts @@ -0,0 +1,53 @@ +interface A { + method1(): this is { + a: 1 + } + method2(): boolean; + method3(): this is { + a: 1 + }; +} +class B implements A { + method1() { } // should error + + method2() { } // should error + + method3() { // should error + return true + } +} + +class C { + method1(): this is { + a: 1 + } { + return true; + } + + method2(): this is { + a: 1 + } { + return true; + } + + method3(): this is { + a: 1 + } { + return true; + } +} + +class D extends C { + method1(): void { // should error + } + + method2(): this is { // should ok + a: 1 + } { + return true; + } + + method3(): boolean { // should error + return true; + } +} \ No newline at end of file