Skip to content

Commit f608fc0

Browse files
authored
check return type of this type predicates (#57341)
1 parent 13e6474 commit f608fc0

9 files changed

+535
-3
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ import {
736736
isThisInTypeQuery,
737737
isThisProperty,
738738
isThisTypeParameter,
739+
isThisTypePredicate,
739740
isTransientSymbol,
740741
isTupleTypeNode,
741742
isTypeAlias,
@@ -21217,7 +21218,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2121721218
if (sourceTypePredicate) {
2121821219
result &= compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes);
2121921220
}
21220-
else if (isIdentifierTypePredicate(targetTypePredicate)) {
21221+
else if (isIdentifierTypePredicate(targetTypePredicate) || isThisTypePredicate(targetTypePredicate)) {
2122121222
if (reportErrors) {
2122221223
errorReporter!(Diagnostics.Signature_0_must_be_a_type_predicate, signatureToString(source));
2122321224
}

src/server/scriptVersionCache.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ export class LineNode implements LineCollection {
591591
if (children.length) this.updateCounts();
592592
}
593593

594-
isLeaf() {
594+
isLeaf(): this is LineLeaf {
595595
return false;
596596
}
597597

@@ -839,7 +839,7 @@ export class LineLeaf implements LineCollection {
839839
constructor(public text: string) {
840840
}
841841

842-
isLeaf() {
842+
isLeaf(): this is LineLeaf {
843843
return true;
844844
}
845845

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
implementArrayInterface.ts(19,5): error TS2416: Property 'every' in type 'MyArray<T>' is not assignable to the same property in base type 'T[]'.
2+
Type '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => boolean' is not assignable to type '{ <S extends T>(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; }'.
3+
Signature '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean' must be a type predicate.
4+
5+
6+
==== implementArrayInterface.ts (1 errors) ====
7+
declare class MyArray<T> implements Array<T> {
8+
toString(): string;
9+
toLocaleString(): string;
10+
concat<U extends T[]>(...items: U[]): T[];
11+
concat(...items: T[]): T[];
12+
join(separator?: string): string;
13+
pop(): T;
14+
push(...items: T[]): number;
15+
reverse(): T[];
16+
shift(): T;
17+
slice(start?: number, end?: number): T[];
18+
sort(compareFn?: (a: T, b: T) => number): this;
19+
splice(start: number): T[];
20+
splice(start: number, deleteCount: number, ...items: T[]): T[];
21+
unshift(...items: T[]): number;
22+
23+
indexOf(searchElement: T, fromIndex?: number): number;
24+
lastIndexOf(searchElement: T, fromIndex?: number): number;
25+
every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
26+
~~~~~
27+
!!! error TS2416: Property 'every' in type 'MyArray<T>' is not assignable to the same property in base type 'T[]'.
28+
!!! error TS2416: Type '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => boolean' is not assignable to type '{ <S extends T>(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; }'.
29+
!!! error TS2416: Signature '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean' must be a type predicate.
30+
some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
31+
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
32+
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
33+
filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
34+
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T;
35+
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
36+
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T;
37+
reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
38+
39+
length: number;
40+
41+
[n: number]: T;
42+
}
43+

tests/baselines/reference/implementArrayInterface.types

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ declare class MyArray<T> implements Array<T> {
117117
>array : T[]
118118
> : ^^^
119119
>thisArg : any
120+
> : ^^^
120121

121122
some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
122123
>some : (callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => boolean
@@ -130,6 +131,7 @@ declare class MyArray<T> implements Array<T> {
130131
>array : T[]
131132
> : ^^^
132133
>thisArg : any
134+
> : ^^^
133135

134136
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
135137
>forEach : (callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any) => void
@@ -143,6 +145,7 @@ declare class MyArray<T> implements Array<T> {
143145
>array : T[]
144146
> : ^^^
145147
>thisArg : any
148+
> : ^^^
146149

147150
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
148151
>map : <U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[]
@@ -156,6 +159,7 @@ declare class MyArray<T> implements Array<T> {
156159
>array : T[]
157160
> : ^^^
158161
>thisArg : any
162+
> : ^^^
159163

160164
filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
161165
>filter : (callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => T[]
@@ -169,6 +173,7 @@ declare class MyArray<T> implements Array<T> {
169173
>array : T[]
170174
> : ^^^
171175
>thisArg : any
176+
> : ^^^
172177

173178
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T;
174179
>reduce : { (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; <U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; }
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
typePredicateInherit.ts(11,3): error TS2416: Property 'method1' in type 'B' is not assignable to the same property in base type 'A'.
2+
Type '() => void' is not assignable to type '() => this is { a: 1; }'.
3+
Signature '(): void' must be a type predicate.
4+
typePredicateInherit.ts(13,3): error TS2416: Property 'method2' in type 'B' is not assignable to the same property in base type 'A'.
5+
Type '() => void' is not assignable to type '() => boolean'.
6+
Type 'void' is not assignable to type 'boolean'.
7+
typePredicateInherit.ts(15,3): error TS2416: Property 'method3' in type 'B' is not assignable to the same property in base type 'A'.
8+
Type '() => boolean' is not assignable to type '() => this is { a: 1; }'.
9+
Signature '(): boolean' must be a type predicate.
10+
typePredicateInherit.ts(41,3): error TS2416: Property 'method1' in type 'D' is not assignable to the same property in base type 'C'.
11+
Type '() => void' is not assignable to type '() => this is { a: 1; }'.
12+
Signature '(): void' must be a type predicate.
13+
typePredicateInherit.ts(50,3): error TS2416: Property 'method3' in type 'D' is not assignable to the same property in base type 'C'.
14+
Type '() => boolean' is not assignable to type '() => this is { a: 1; }'.
15+
Signature '(): boolean' must be a type predicate.
16+
17+
18+
==== typePredicateInherit.ts (5 errors) ====
19+
interface A {
20+
method1(): this is {
21+
a: 1
22+
}
23+
method2(): boolean;
24+
method3(): this is {
25+
a: 1
26+
};
27+
}
28+
class B implements A {
29+
method1() { } // should error
30+
~~~~~~~
31+
!!! error TS2416: Property 'method1' in type 'B' is not assignable to the same property in base type 'A'.
32+
!!! error TS2416: Type '() => void' is not assignable to type '() => this is { a: 1; }'.
33+
!!! error TS2416: Signature '(): void' must be a type predicate.
34+
35+
method2() { } // should error
36+
~~~~~~~
37+
!!! error TS2416: Property 'method2' in type 'B' is not assignable to the same property in base type 'A'.
38+
!!! error TS2416: Type '() => void' is not assignable to type '() => boolean'.
39+
!!! error TS2416: Type 'void' is not assignable to type 'boolean'.
40+
41+
method3() { // should error
42+
~~~~~~~
43+
!!! error TS2416: Property 'method3' in type 'B' is not assignable to the same property in base type 'A'.
44+
!!! error TS2416: Type '() => boolean' is not assignable to type '() => this is { a: 1; }'.
45+
!!! error TS2416: Signature '(): boolean' must be a type predicate.
46+
return true
47+
}
48+
}
49+
50+
class C {
51+
method1(): this is {
52+
a: 1
53+
} {
54+
return true;
55+
}
56+
57+
method2(): this is {
58+
a: 1
59+
} {
60+
return true;
61+
}
62+
63+
method3(): this is {
64+
a: 1
65+
} {
66+
return true;
67+
}
68+
}
69+
70+
class D extends C {
71+
method1(): void { // should error
72+
~~~~~~~
73+
!!! error TS2416: Property 'method1' in type 'D' is not assignable to the same property in base type 'C'.
74+
!!! error TS2416: Type '() => void' is not assignable to type '() => this is { a: 1; }'.
75+
!!! error TS2416: Signature '(): void' must be a type predicate.
76+
}
77+
78+
method2(): this is { // should ok
79+
a: 1
80+
} {
81+
return true;
82+
}
83+
84+
method3(): boolean { // should error
85+
~~~~~~~
86+
!!! error TS2416: Property 'method3' in type 'D' is not assignable to the same property in base type 'C'.
87+
!!! error TS2416: Type '() => boolean' is not assignable to type '() => this is { a: 1; }'.
88+
!!! error TS2416: Signature '(): boolean' must be a type predicate.
89+
return true;
90+
}
91+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//// [tests/cases/compiler/typePredicateInherit.ts] ////
2+
3+
//// [typePredicateInherit.ts]
4+
interface A {
5+
method1(): this is {
6+
a: 1
7+
}
8+
method2(): boolean;
9+
method3(): this is {
10+
a: 1
11+
};
12+
}
13+
class B implements A {
14+
method1() { } // should error
15+
16+
method2() { } // should error
17+
18+
method3() { // should error
19+
return true
20+
}
21+
}
22+
23+
class C {
24+
method1(): this is {
25+
a: 1
26+
} {
27+
return true;
28+
}
29+
30+
method2(): this is {
31+
a: 1
32+
} {
33+
return true;
34+
}
35+
36+
method3(): this is {
37+
a: 1
38+
} {
39+
return true;
40+
}
41+
}
42+
43+
class D extends C {
44+
method1(): void { // should error
45+
}
46+
47+
method2(): this is { // should ok
48+
a: 1
49+
} {
50+
return true;
51+
}
52+
53+
method3(): boolean { // should error
54+
return true;
55+
}
56+
}
57+
58+
//// [typePredicateInherit.js]
59+
var __extends = (this && this.__extends) || (function () {
60+
var extendStatics = function (d, b) {
61+
extendStatics = Object.setPrototypeOf ||
62+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
63+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
64+
return extendStatics(d, b);
65+
};
66+
return function (d, b) {
67+
if (typeof b !== "function" && b !== null)
68+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
69+
extendStatics(d, b);
70+
function __() { this.constructor = d; }
71+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
72+
};
73+
})();
74+
var B = /** @class */ (function () {
75+
function B() {
76+
}
77+
B.prototype.method1 = function () { }; // should error
78+
B.prototype.method2 = function () { }; // should error
79+
B.prototype.method3 = function () {
80+
return true;
81+
};
82+
return B;
83+
}());
84+
var C = /** @class */ (function () {
85+
function C() {
86+
}
87+
C.prototype.method1 = function () {
88+
return true;
89+
};
90+
C.prototype.method2 = function () {
91+
return true;
92+
};
93+
C.prototype.method3 = function () {
94+
return true;
95+
};
96+
return C;
97+
}());
98+
var D = /** @class */ (function (_super) {
99+
__extends(D, _super);
100+
function D() {
101+
return _super !== null && _super.apply(this, arguments) || this;
102+
}
103+
D.prototype.method1 = function () {
104+
};
105+
D.prototype.method2 = function () {
106+
return true;
107+
};
108+
D.prototype.method3 = function () {
109+
return true;
110+
};
111+
return D;
112+
}(C));

0 commit comments

Comments
 (0)