Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make type guard function types invariant in the type guarded for. #27686

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11883,7 +11883,10 @@ namespace ts {
}
}

const related = compareTypes(source.type, target.type, reportErrors);
let related = compareTypes(source.type, target.type, reportErrors);
if (related) {
related &= compareTypes(target.type, source.type, reportErrors);
}
if (related === Ternary.False && reportErrors) {
errorReporter!(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target));
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/documentHighlights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ namespace ts.DocumentHighlights {
case SyntaxKind.SetKeyword:
return getFromAllDeclarations(isAccessor, [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword]);
case SyntaxKind.AwaitKeyword:
return useParent(node.parent, isAwaitExpression, getAsyncAndAwaitOccurrences);
return useParent<AwaitExpression>(node.parent, isAwaitExpression, getAsyncAndAwaitOccurrences);
case SyntaxKind.AsyncKeyword:
return highlightSpans(getAsyncAndAwaitOccurrences(node));
case SyntaxKind.YieldKeyword:
Expand Down
106 changes: 58 additions & 48 deletions tests/baselines/reference/typeGuardFunctionErrors.errors.txt

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion tests/baselines/reference/typeGuardFunctionErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function hasNonMathcingGenericType<T>(a: string): a is T[] {
let a: A;
let b: B;

declare function isA(p1): p1 is A;
declare function isB(p1): p1 is B;
declare function isC(p1): p1 is C;
declare function funA(p1: any, p2: any): p1 is B;
Expand All @@ -71,8 +72,10 @@ if (hasNoTypeGuard(a)) {
}

// Type predicate type is not assignable
declare function acceptingDifferentSignatureTypeGuardFunction(p1: (p1) => p1 is B);
declare function acceptingDifferentSignatureTypeGuardFunction(p1: (p1) => p1 is A);
acceptingDifferentSignatureTypeGuardFunction(isC);
declare function acceptingDifferentSignatureTypeGuardFunction2(p1: (p1) => p1 is C);
acceptingDifferentSignatureTypeGuardFunction2(isA);

// Boolean not assignable to type guard
var assign1: (p1, p2) => p1 is A;
Expand Down Expand Up @@ -240,6 +243,7 @@ if (hasNoTypeGuard(a)) {
a.propB;
}
acceptingDifferentSignatureTypeGuardFunction(isC);
acceptingDifferentSignatureTypeGuardFunction2(isA);
// Boolean not assignable to type guard
var assign1;
assign1 = function (p1, p2) {
Expand Down
305 changes: 161 additions & 144 deletions tests/baselines/reference/typeGuardFunctionErrors.symbols

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions tests/baselines/reference/typeGuardFunctionErrors.types
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ let a: A;
let b: B;
>b : B

declare function isA(p1): p1 is A;
>isA : (p1: any) => p1 is A
>p1 : any

declare function isB(p1): p1 is B;
>isB : (p1: any) => p1 is B
>p1 : any
Expand Down Expand Up @@ -154,16 +158,26 @@ if (hasNoTypeGuard(a)) {
}

// Type predicate type is not assignable
declare function acceptingDifferentSignatureTypeGuardFunction(p1: (p1) => p1 is B);
>acceptingDifferentSignatureTypeGuardFunction : (p1: (p1: any) => p1 is B) => any
>p1 : (p1: any) => p1 is B
declare function acceptingDifferentSignatureTypeGuardFunction(p1: (p1) => p1 is A);
>acceptingDifferentSignatureTypeGuardFunction : (p1: (p1: any) => p1 is A) => any
>p1 : (p1: any) => p1 is A
>p1 : any

acceptingDifferentSignatureTypeGuardFunction(isC);
>acceptingDifferentSignatureTypeGuardFunction(isC) : any
>acceptingDifferentSignatureTypeGuardFunction : (p1: (p1: any) => p1 is B) => any
>acceptingDifferentSignatureTypeGuardFunction : (p1: (p1: any) => p1 is A) => any
>isC : (p1: any) => p1 is C

declare function acceptingDifferentSignatureTypeGuardFunction2(p1: (p1) => p1 is C);
>acceptingDifferentSignatureTypeGuardFunction2 : (p1: (p1: any) => p1 is C) => any
>p1 : (p1: any) => p1 is C
>p1 : any

acceptingDifferentSignatureTypeGuardFunction2(isA);
>acceptingDifferentSignatureTypeGuardFunction2(isA) : any
>acceptingDifferentSignatureTypeGuardFunction2 : (p1: (p1: any) => p1 is C) => any
>isA : (p1: any) => p1 is A

// Boolean not assignable to type guard
var assign1: (p1, p2) => p1 is A;
>assign1 : (p1: any, p2: any) => p1 is A
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts(79,1
!!! error TS1228: A type predicate is only allowed in return type position for functions and methods.
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
~~~~~~~~~~~~~~~~~~~~~~~~~~
isNetworked: this is Networked;
~~~~~~~~~~~~~~~~~
!!! error TS1228: A type predicate is only allowed in return type position for functions and methods.
constructor(public path: string) {}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/typeGuardOfFormThisMember.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Test {
get isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
constructor(public path: string) {}
}

Expand Down Expand Up @@ -180,7 +180,7 @@ declare namespace Test {
isFSO: this is FileSystemObject;
isFile: this is File;
readonly isDirectory: this is Directory;
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
constructor(path: string);
}
class File extends FileSystemObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace Test {
>this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16))
>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 19, 2))
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3))
>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 22, 2))

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/typeGuardOfFormThisMember.types
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace Test {
>this : this
>Directory : typeof Directory
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
>isNetworked : boolean

constructor(public path: string) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.t
!!! error TS1228: A type predicate is only allowed in return type position for functions and methods.
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
~~~~~~~~~~~~~~~~~~~~~~~~~~
isNetworked: this is Networked;
~~~~~~~~~~~~~~~~~
!!! error TS1228: A type predicate is only allowed in return type position for functions and methods.
constructor(public path: string) {}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/typeGuardOfFormThisMemberErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Test {
get isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
constructor(public path: string) {}
}

Expand Down Expand Up @@ -105,7 +105,7 @@ declare namespace Test {
isFSO: this is FileSystemObject;
isFile: this is File;
readonly isDirectory: this is Directory;
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
constructor(path: string);
}
class File extends FileSystemObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace Test {
>this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMemberErrors.ts, 1, 16))
>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMemberErrors.ts, 19, 2))
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMemberErrors.ts, 12, 3))
>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMemberErrors.ts, 22, 2))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace Test {
>this : this
>Directory : typeof Directory
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
>isNetworked : boolean

constructor(public path: string) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function hasNonMathcingGenericType<T>(a: string): a is T[] {
let a: A;
let b: B;

declare function isA(p1): p1 is A;
declare function isB(p1): p1 is B;
declare function isC(p1): p1 is C;
declare function funA(p1: any, p2: any): p1 is B;
Expand All @@ -71,8 +72,10 @@ if (hasNoTypeGuard(a)) {
}

// Type predicate type is not assignable
declare function acceptingDifferentSignatureTypeGuardFunction(p1: (p1) => p1 is B);
declare function acceptingDifferentSignatureTypeGuardFunction(p1: (p1) => p1 is A);
acceptingDifferentSignatureTypeGuardFunction(isC);
declare function acceptingDifferentSignatureTypeGuardFunction2(p1: (p1) => p1 is C);
acceptingDifferentSignatureTypeGuardFunction2(isA);

// Boolean not assignable to type guard
var assign1: (p1, p2) => p1 is A;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Test {
get isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
constructor(public path: string) {}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Test {
get isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked: this is (Networked & this);
isNetworked: this is Networked;
constructor(public path: string) {}
}

Expand Down
8 changes: 4 additions & 4 deletions tests/cases/fourslash/thisPredicateFunctionCompletions01.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//// isDirectory(): this is Directory {
//// return this instanceof Directory;
//// }
//// isNetworked(): this is (Networked & this) {
//// isNetworked(): this is Networked {
//// return !!(this as Networked).host;
//// }
//// constructor(public path: string) {}
Expand Down Expand Up @@ -43,8 +43,8 @@
const common: ReadonlyArray<string> = ["isFile", "isDirectory", "isNetworked", "path"];
verify.completions(
{ marker: "1", exact: ["content", ...common] },
{ marker: "2", exact: ["host", "content", ...common] },
{ marker: "2", exact: ["content", ...common, "host"] },
{ marker: "3", exact: ["children", ...common] },
{ marker: "4", exact: ["host", "children", ...common] },
{ marker: "5", exact: ["host", ...common] },
{ marker: "4", exact: ["children", ...common, "host"] },
{ marker: "5", exact: [...common, "host"] },
);
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//// contents: T;
//// isSundries(): this is Crate<Sundries>;
//// isSupplies(): this is Crate<Supplies>;
//// isPackedTight(): this is (this & {extraContents: T});
//// isPackedTight<U>(this: this & Crate<U>): this is {extraContents: U};
//// }
//// const crate: Crate<any>;
//// if (crate.isPackedTight()) {
Expand Down
10 changes: 5 additions & 5 deletions tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//// /*2*/isDirectory(): this is Directory {
//// return this instanceof Directory;
//// }
//// /*3*/isNetworked(): this is (Networked & this) {
//// /*3*/isNetworked(): this is Networked {
//// return !!(this as Networked).host;
//// }
//// constructor(public path: string) {}
Expand Down Expand Up @@ -43,11 +43,11 @@
verify.quickInfos({
1: "(method) FileSystemObject.isFile(): this is Item",
2: "(method) FileSystemObject.isDirectory(): this is Directory",
3: "(method) FileSystemObject.isNetworked(): this is Networked & this",
3: "(method) FileSystemObject.isNetworked(): this is Networked",

4: "(method) FileSystemObject.isFile(): this is Item",
5: "(method) FileSystemObject.isNetworked(): this is Networked & Item",
5: "(method) FileSystemObject.isNetworked(): this is Networked",
6: "(method) FileSystemObject.isDirectory(): this is Directory",
7: "(method) FileSystemObject.isNetworked(): this is Networked & Directory",
8: "(method) FileSystemObject.isNetworked(): this is Networked & FileSystemObject"
7: "(method) FileSystemObject.isNetworked(): this is Networked",
8: "(method) FileSystemObject.isNetworked(): this is Networked"
});
12 changes: 6 additions & 6 deletions tests/cases/fourslash/thisPredicateFunctionQuickInfo02.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//// contents: T;
//// /*1*/isSundries(): this is Crate<Sundries>;
//// /*2*/isSupplies(): this is Crate<Supplies>;
//// /*3*/isPackedTight(): this is (this & {extraContents: T});
//// /*3*/isPackedTight<U>(this: this & Crate<U>): this is {extraContents: U};
//// }
//// const crate: Crate<any>;
//// if (crate.isPackedTight/*4*/()) {
Expand All @@ -34,18 +34,18 @@
verify.quickInfos({
1: "(method) Crate<T>.isSundries(): this is Crate<Sundries>",
2: "(method) Crate<T>.isSupplies(): this is Crate<Supplies>",
3: `(method) Crate<T>.isPackedTight(): this is this & {
extraContents: T;
3: `(method) Crate<T>.isPackedTight<U>(this: this & Crate<U>): this is {
extraContents: U;
}`,
4: `(method) Crate<any>.isPackedTight(): this is Crate<any> & {
4: `(method) Crate<any>.isPackedTight<any>(this: Crate<any>): this is {
extraContents: any;
}`,
5: "(method) Crate<any>.isSundries(): this is Crate<Sundries>",
6: `(method) Crate<Sundries>.isPackedTight(): this is Crate<Sundries> & {
6: `(method) Crate<Sundries>.isPackedTight<Sundries>(this: Crate<Sundries>): this is {
extraContents: Sundries;
}`,
7: "(method) Crate<any>.isSupplies(): this is Crate<Supplies>",
8: `(method) Crate<Supplies>.isPackedTight(): this is Crate<Supplies> & {
8: `(method) Crate<Supplies>.isPackedTight<Supplies>(this: Crate<Supplies>): this is {
extraContents: Supplies;
}`
});