check return type of this type predicates (#57341)

This commit is contained in:
Zzzen 2024-04-09 04:37:24 +08:00 committed by GitHub
parent 13e64740c9
commit f608fc0a10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 535 additions and 3 deletions

View File

@ -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));
}

View File

@ -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;
}

View File

@ -0,0 +1,43 @@
implementArrayInterface.ts(19,5): error TS2416: Property 'every' in type 'MyArray<T>' 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 '{ <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; }'.
Signature '(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean' must be a type predicate.
==== implementArrayInterface.ts (1 errors) ====
declare class MyArray<T> implements Array<T> {
toString(): string;
toLocaleString(): string;
concat<U extends T[]>(...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<T>' 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 '{ <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; }'.
!!! 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<U>(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<U>(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<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
length: number;
[n: number]: T;
}

View File

@ -117,6 +117,7 @@ declare class MyArray<T> implements Array<T> {
>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<T> implements Array<T> {
>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<T> implements Array<T> {
>array : T[]
> : ^^^
>thisArg : any
> : ^^^
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
>map : <U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[]
@ -156,6 +159,7 @@ declare class MyArray<T> implements Array<T> {
>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<T> implements Array<T> {
>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; <U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; }

View File

@ -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;
}
}

View File

@ -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));

View File

@ -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;
}
}

View File

@ -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
> : ^^^^
}
}

View File

@ -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;
}
}