diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 80b61edd3657a..823675666af65 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -38778,7 +38778,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
- function getTypePredicateFromBody(func: FunctionLikeDeclaration): TypePredicate | undefined {
+ function getTypePredicateFromBody(func: FunctionLikeDeclaration, contextualTypePredicate?: IdentifierTypePredicate): TypePredicate | undefined {
switch (func.kind) {
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
@@ -38800,41 +38800,43 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
});
if (bailedEarly || !singleReturn || functionHasImplicitReturn(func)) return undefined;
}
- return checkIfExpressionRefinesAnyParameter(func, singleReturn);
- }
-
- function checkIfExpressionRefinesAnyParameter(func: FunctionLikeDeclaration, expr: Expression): TypePredicate | undefined {
- expr = skipParentheses(expr, /*excludeJSDocTypeAssertions*/ true);
+ const expr = skipParentheses(singleReturn, /*excludeJSDocTypeAssertions*/ true);
const returnType = checkExpressionCached(expr);
- if (!(returnType.flags & TypeFlags.Boolean)) return undefined;
-
- return forEach(func.parameters, (param, i) => {
- const initType = getTypeOfSymbol(param.symbol);
- if (!initType || initType.flags & TypeFlags.Boolean || !isIdentifier(param.name) || isSymbolAssigned(param.symbol) || isRestParameter(param)) {
- // Refining "x: boolean" to "x is true" or "x is false" isn't useful.
- return;
- }
- const trueType = checkIfExpressionRefinesParameter(func, expr, param, initType);
- if (trueType) {
- return createTypePredicate(TypePredicateKind.Identifier, unescapeLeadingUnderscores(param.name.escapedText), i, trueType);
+ if (contextualTypePredicate && returnType.flags & TypeFlags.BooleanLiteral) {
+ const param = func.parameters[contextualTypePredicate.parameterIndex];
+ if (!isIdentifier(param.name)) {
+ return undefined;
}
- });
+ const refinedType = (returnType as IntrinsicType).intrinsicName === "true" ? getTypeOfSymbol(param.symbol) : neverType;
+ return createTypePredicate(TypePredicateKind.Identifier, unescapeLeadingUnderscores(param.name.escapedText), contextualTypePredicate.parameterIndex, refinedType);
+ }
+ if (!(returnType.flags & TypeFlags.Boolean)) return undefined;
+ return contextualTypePredicate ?
+ getTypePredicateIfRefinesParameterAtIndex(func, expr, contextualTypePredicate, contextualTypePredicate.parameterIndex) :
+ forEach(func.parameters, (_, i) => getTypePredicateIfRefinesParameterAtIndex(func, expr, contextualTypePredicate, i));
}
- function checkIfExpressionRefinesParameter(func: FunctionLikeDeclaration, expr: Expression, param: ParameterDeclaration, initType: Type): Type | undefined {
+ function getTypePredicateIfRefinesParameterAtIndex(func: FunctionLikeDeclaration, expr: Expression, contextualTypePredicate: IdentifierTypePredicate | undefined, parameterIndex: number): TypePredicate | undefined {
+ const param = func.parameters[parameterIndex];
+ const initType = getTypeOfSymbol(param.symbol);
+ if (!initType || initType.flags & TypeFlags.Boolean || !isIdentifier(param.name) || isSymbolAssigned(param.symbol) || isRestParameter(param)) {
+ // Refining "x: boolean" to "x is true" or "x is false" isn't useful.
+ return;
+ }
const antecedent = canHaveFlowNode(expr) && expr.flowNode ||
expr.parent.kind === SyntaxKind.ReturnStatement && (expr.parent as ReturnStatement).flowNode ||
createFlowNode(FlowFlags.Start, /*node*/ undefined, /*antecedent*/ undefined);
const trueCondition = createFlowNode(FlowFlags.TrueCondition, expr, antecedent);
const trueType = getFlowTypeOfReference(param.name, initType, initType, func, trueCondition);
- if (trueType === initType) return undefined;
-
+ if (!contextualTypePredicate && trueType === initType) {
+ return undefined;
+ }
// "x is T" means that x is T if and only if it returns true. If it returns false then x is not T.
// This means that if the function is called with an argument of type trueType, there can't be anything left in the `else` branch. It must reduce to `never`.
const falseCondition = createFlowNode(FlowFlags.FalseCondition, expr, antecedent);
const falseSubtype = getFlowTypeOfReference(param.name, initType, trueType, func, falseCondition);
- return falseSubtype.flags & TypeFlags.Never ? trueType : undefined;
+ return falseSubtype.flags & TypeFlags.Never ? createTypePredicate(TypePredicateKind.Identifier, unescapeLeadingUnderscores(param.name.escapedText), parameterIndex, trueType) : undefined;
}
/**
@@ -38978,10 +38980,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!);
}
}
- if (contextualSignature && !getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) {
- const returnType = getReturnTypeFromBody(node, checkMode);
- if (!signature.resolvedReturnType) {
- signature.resolvedReturnType = returnType;
+ if (contextualSignature && !getReturnTypeFromAnnotation(node)) {
+ const returnType = signature.resolvedReturnType ?? getReturnTypeFromBody(node, checkMode);
+ signature.resolvedReturnType ??= returnType;
+ if (signature.resolvedReturnType.flags && TypeFlags.Boolean && contextualSignature.resolvedTypePredicate && contextualSignature.resolvedTypePredicate !== noTypePredicate && contextualSignature.resolvedTypePredicate.kind === TypePredicateKind.Identifier) {
+ signature.resolvedTypePredicate ??= getTypePredicateFromBody(node, contextualSignature.resolvedTypePredicate) ?? noTypePredicate;
}
}
checkSignatureDeclaration(node);
diff --git a/tests/baselines/reference/inferContextualTypePredicates1.errors.txt b/tests/baselines/reference/inferContextualTypePredicates1.errors.txt
new file mode 100644
index 0000000000000..90e9f01166123
--- /dev/null
+++ b/tests/baselines/reference/inferContextualTypePredicates1.errors.txt
@@ -0,0 +1,45 @@
+inferContextualTypePredicates1.ts(17,7): error TS2322: Type '(a: string | null, b: string | null) => boolean' is not assignable to type '(a: string | null, b: string | null) => b is string'.
+ Signature '(a: string | null, b: string | null): boolean' must be a type predicate.
+
+
+==== inferContextualTypePredicates1.ts (1 errors) ====
+ type Foo = { type: "foo"; foo: number };
+ type Bar = { type: "bar"; bar: string };
+
+ declare function skipIf(
+ as: A[],
+ predicate: (a: A) => a is B,
+ ): Exclude[];
+
+ declare const items: (Foo | Bar)[];
+
+ const r1 = skipIf(items, (item) => item.type === "foo"); // ok
+ const r2 = skipIf(items, (item) => item.type === "foo" || item.type === "bar"); // ok
+ const r3 = skipIf(items, (item) => false); // ok
+ const r4 = skipIf(items, (item) => true); // ok
+
+ const pred1: (a: string | null, b: string | null) => b is string = (a, b) => typeof b === 'string'; // ok
+ const pred2: (a: string | null, b: string | null) => b is string = (a, b) => typeof a === 'string'; // error
+ ~~~~~
+!!! error TS2322: Type '(a: string | null, b: string | null) => boolean' is not assignable to type '(a: string | null, b: string | null) => b is string'.
+!!! error TS2322: Signature '(a: string | null, b: string | null): boolean' must be a type predicate.
+
+ export declare function every(array: readonly T[], callback: (element: T, index: number) => element is U): array is readonly U[];
+ export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => element is U): array is readonly U[] | undefined;
+ export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean;
+
+ type Type = {
+ kind: number;
+ flags: number;
+ };
+
+ function testEvery(typeSet: Type[]) {
+ if (every(typeSet, (t) => t.kind === 1)) {
+ return 1;
+ }
+ if (every(typeSet, (t) => t.kind === 2)) {
+ return 2;
+ }
+ return -1;
+ }
+
\ No newline at end of file
diff --git a/tests/baselines/reference/inferContextualTypePredicates1.symbols b/tests/baselines/reference/inferContextualTypePredicates1.symbols
new file mode 100644
index 0000000000000..052286a7a24b8
--- /dev/null
+++ b/tests/baselines/reference/inferContextualTypePredicates1.symbols
@@ -0,0 +1,172 @@
+//// [tests/cases/compiler/inferContextualTypePredicates1.ts] ////
+
+=== inferContextualTypePredicates1.ts ===
+type Foo = { type: "foo"; foo: number };
+>Foo : Symbol(Foo, Decl(inferContextualTypePredicates1.ts, 0, 0))
+>type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 0, 12))
+>foo : Symbol(foo, Decl(inferContextualTypePredicates1.ts, 0, 25))
+
+type Bar = { type: "bar"; bar: string };
+>Bar : Symbol(Bar, Decl(inferContextualTypePredicates1.ts, 0, 40))
+>type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 1, 12))
+>bar : Symbol(bar, Decl(inferContextualTypePredicates1.ts, 1, 25))
+
+declare function skipIf(
+>skipIf : Symbol(skipIf, Decl(inferContextualTypePredicates1.ts, 1, 40))
+>A : Symbol(A, Decl(inferContextualTypePredicates1.ts, 3, 24))
+>B : Symbol(B, Decl(inferContextualTypePredicates1.ts, 3, 26))
+>A : Symbol(A, Decl(inferContextualTypePredicates1.ts, 3, 24))
+
+ as: A[],
+>as : Symbol(as, Decl(inferContextualTypePredicates1.ts, 3, 40))
+>A : Symbol(A, Decl(inferContextualTypePredicates1.ts, 3, 24))
+
+ predicate: (a: A) => a is B,
+>predicate : Symbol(predicate, Decl(inferContextualTypePredicates1.ts, 4, 10))
+>a : Symbol(a, Decl(inferContextualTypePredicates1.ts, 5, 14))
+>A : Symbol(A, Decl(inferContextualTypePredicates1.ts, 3, 24))
+>a : Symbol(a, Decl(inferContextualTypePredicates1.ts, 5, 14))
+>B : Symbol(B, Decl(inferContextualTypePredicates1.ts, 3, 26))
+
+): Exclude[];
+>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
+>A : Symbol(A, Decl(inferContextualTypePredicates1.ts, 3, 24))
+>B : Symbol(B, Decl(inferContextualTypePredicates1.ts, 3, 26))
+
+declare const items: (Foo | Bar)[];
+>items : Symbol(items, Decl(inferContextualTypePredicates1.ts, 8, 13))
+>Foo : Symbol(Foo, Decl(inferContextualTypePredicates1.ts, 0, 0))
+>Bar : Symbol(Bar, Decl(inferContextualTypePredicates1.ts, 0, 40))
+
+const r1 = skipIf(items, (item) => item.type === "foo"); // ok
+>r1 : Symbol(r1, Decl(inferContextualTypePredicates1.ts, 10, 5))
+>skipIf : Symbol(skipIf, Decl(inferContextualTypePredicates1.ts, 1, 40))
+>items : Symbol(items, Decl(inferContextualTypePredicates1.ts, 8, 13))
+>item : Symbol(item, Decl(inferContextualTypePredicates1.ts, 10, 26))
+>item.type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 0, 12), Decl(inferContextualTypePredicates1.ts, 1, 12))
+>item : Symbol(item, Decl(inferContextualTypePredicates1.ts, 10, 26))
+>type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 0, 12), Decl(inferContextualTypePredicates1.ts, 1, 12))
+
+const r2 = skipIf(items, (item) => item.type === "foo" || item.type === "bar"); // ok
+>r2 : Symbol(r2, Decl(inferContextualTypePredicates1.ts, 11, 5))
+>skipIf : Symbol(skipIf, Decl(inferContextualTypePredicates1.ts, 1, 40))
+>items : Symbol(items, Decl(inferContextualTypePredicates1.ts, 8, 13))
+>item : Symbol(item, Decl(inferContextualTypePredicates1.ts, 11, 26))
+>item.type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 0, 12), Decl(inferContextualTypePredicates1.ts, 1, 12))
+>item : Symbol(item, Decl(inferContextualTypePredicates1.ts, 11, 26))
+>type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 0, 12), Decl(inferContextualTypePredicates1.ts, 1, 12))
+>item.type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 1, 12))
+>item : Symbol(item, Decl(inferContextualTypePredicates1.ts, 11, 26))
+>type : Symbol(type, Decl(inferContextualTypePredicates1.ts, 1, 12))
+
+const r3 = skipIf(items, (item) => false); // ok
+>r3 : Symbol(r3, Decl(inferContextualTypePredicates1.ts, 12, 5))
+>skipIf : Symbol(skipIf, Decl(inferContextualTypePredicates1.ts, 1, 40))
+>items : Symbol(items, Decl(inferContextualTypePredicates1.ts, 8, 13))
+>item : Symbol(item, Decl(inferContextualTypePredicates1.ts, 12, 26))
+
+const r4 = skipIf(items, (item) => true); // ok
+>r4 : Symbol(r4, Decl(inferContextualTypePredicates1.ts, 13, 5))
+>skipIf : Symbol(skipIf, Decl(inferContextualTypePredicates1.ts, 1, 40))
+>items : Symbol(items, Decl(inferContextualTypePredicates1.ts, 8, 13))
+>item : Symbol(item, Decl(inferContextualTypePredicates1.ts, 13, 26))
+
+const pred1: (a: string | null, b: string | null) => b is string = (a, b) => typeof b === 'string'; // ok
+>pred1 : Symbol(pred1, Decl(inferContextualTypePredicates1.ts, 15, 5))
+>a : Symbol(a, Decl(inferContextualTypePredicates1.ts, 15, 14))
+>b : Symbol(b, Decl(inferContextualTypePredicates1.ts, 15, 31))
+>b : Symbol(b, Decl(inferContextualTypePredicates1.ts, 15, 31))
+>a : Symbol(a, Decl(inferContextualTypePredicates1.ts, 15, 68))
+>b : Symbol(b, Decl(inferContextualTypePredicates1.ts, 15, 70))
+>b : Symbol(b, Decl(inferContextualTypePredicates1.ts, 15, 70))
+
+const pred2: (a: string | null, b: string | null) => b is string = (a, b) => typeof a === 'string'; // error
+>pred2 : Symbol(pred2, Decl(inferContextualTypePredicates1.ts, 16, 5))
+>a : Symbol(a, Decl(inferContextualTypePredicates1.ts, 16, 14))
+>b : Symbol(b, Decl(inferContextualTypePredicates1.ts, 16, 31))
+>b : Symbol(b, Decl(inferContextualTypePredicates1.ts, 16, 31))
+>a : Symbol(a, Decl(inferContextualTypePredicates1.ts, 16, 68))
+>b : Symbol(b, Decl(inferContextualTypePredicates1.ts, 16, 70))
+>a : Symbol(a, Decl(inferContextualTypePredicates1.ts, 16, 68))
+
+export declare function every(array: readonly T[], callback: (element: T, index: number) => element is U): array is readonly U[];
+>every : Symbol(every, Decl(inferContextualTypePredicates1.ts, 16, 99), Decl(inferContextualTypePredicates1.ts, 18, 145), Decl(inferContextualTypePredicates1.ts, 19, 169))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 18, 30))
+>U : Symbol(U, Decl(inferContextualTypePredicates1.ts, 18, 32))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 18, 30))
+>array : Symbol(array, Decl(inferContextualTypePredicates1.ts, 18, 46))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 18, 30))
+>callback : Symbol(callback, Decl(inferContextualTypePredicates1.ts, 18, 66))
+>element : Symbol(element, Decl(inferContextualTypePredicates1.ts, 18, 78))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 18, 30))
+>index : Symbol(index, Decl(inferContextualTypePredicates1.ts, 18, 89))
+>element : Symbol(element, Decl(inferContextualTypePredicates1.ts, 18, 78))
+>U : Symbol(U, Decl(inferContextualTypePredicates1.ts, 18, 32))
+>array : Symbol(array, Decl(inferContextualTypePredicates1.ts, 18, 46))
+>U : Symbol(U, Decl(inferContextualTypePredicates1.ts, 18, 32))
+
+export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => element is U): array is readonly U[] | undefined;
+>every : Symbol(every, Decl(inferContextualTypePredicates1.ts, 16, 99), Decl(inferContextualTypePredicates1.ts, 18, 145), Decl(inferContextualTypePredicates1.ts, 19, 169))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 19, 30))
+>U : Symbol(U, Decl(inferContextualTypePredicates1.ts, 19, 32))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 19, 30))
+>array : Symbol(array, Decl(inferContextualTypePredicates1.ts, 19, 46))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 19, 30))
+>callback : Symbol(callback, Decl(inferContextualTypePredicates1.ts, 19, 78))
+>element : Symbol(element, Decl(inferContextualTypePredicates1.ts, 19, 90))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 19, 30))
+>index : Symbol(index, Decl(inferContextualTypePredicates1.ts, 19, 101))
+>element : Symbol(element, Decl(inferContextualTypePredicates1.ts, 19, 90))
+>U : Symbol(U, Decl(inferContextualTypePredicates1.ts, 19, 32))
+>array : Symbol(array, Decl(inferContextualTypePredicates1.ts, 19, 46))
+>U : Symbol(U, Decl(inferContextualTypePredicates1.ts, 19, 32))
+
+export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean;
+>every : Symbol(every, Decl(inferContextualTypePredicates1.ts, 16, 99), Decl(inferContextualTypePredicates1.ts, 18, 145), Decl(inferContextualTypePredicates1.ts, 19, 169))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 20, 30))
+>array : Symbol(array, Decl(inferContextualTypePredicates1.ts, 20, 33))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 20, 30))
+>callback : Symbol(callback, Decl(inferContextualTypePredicates1.ts, 20, 65))
+>element : Symbol(element, Decl(inferContextualTypePredicates1.ts, 20, 77))
+>T : Symbol(T, Decl(inferContextualTypePredicates1.ts, 20, 30))
+>index : Symbol(index, Decl(inferContextualTypePredicates1.ts, 20, 88))
+
+type Type = {
+>Type : Symbol(Type, Decl(inferContextualTypePredicates1.ts, 20, 125))
+
+ kind: number;
+>kind : Symbol(kind, Decl(inferContextualTypePredicates1.ts, 22, 13))
+
+ flags: number;
+>flags : Symbol(flags, Decl(inferContextualTypePredicates1.ts, 23, 15))
+
+};
+
+function testEvery(typeSet: Type[]) {
+>testEvery : Symbol(testEvery, Decl(inferContextualTypePredicates1.ts, 25, 2))
+>typeSet : Symbol(typeSet, Decl(inferContextualTypePredicates1.ts, 27, 19))
+>Type : Symbol(Type, Decl(inferContextualTypePredicates1.ts, 20, 125))
+
+ if (every(typeSet, (t) => t.kind === 1)) {
+>every : Symbol(every, Decl(inferContextualTypePredicates1.ts, 16, 99), Decl(inferContextualTypePredicates1.ts, 18, 145), Decl(inferContextualTypePredicates1.ts, 19, 169))
+>typeSet : Symbol(typeSet, Decl(inferContextualTypePredicates1.ts, 27, 19))
+>t : Symbol(t, Decl(inferContextualTypePredicates1.ts, 28, 22))
+>t.kind : Symbol(kind, Decl(inferContextualTypePredicates1.ts, 22, 13))
+>t : Symbol(t, Decl(inferContextualTypePredicates1.ts, 28, 22))
+>kind : Symbol(kind, Decl(inferContextualTypePredicates1.ts, 22, 13))
+
+ return 1;
+ }
+ if (every(typeSet, (t) => t.kind === 2)) {
+>every : Symbol(every, Decl(inferContextualTypePredicates1.ts, 16, 99), Decl(inferContextualTypePredicates1.ts, 18, 145), Decl(inferContextualTypePredicates1.ts, 19, 169))
+>typeSet : Symbol(typeSet, Decl(inferContextualTypePredicates1.ts, 27, 19))
+>t : Symbol(t, Decl(inferContextualTypePredicates1.ts, 31, 22))
+>t.kind : Symbol(kind, Decl(inferContextualTypePredicates1.ts, 22, 13))
+>t : Symbol(t, Decl(inferContextualTypePredicates1.ts, 31, 22))
+>kind : Symbol(kind, Decl(inferContextualTypePredicates1.ts, 22, 13))
+
+ return 2;
+ }
+ return -1;
+}
+
diff --git a/tests/baselines/reference/inferContextualTypePredicates1.types b/tests/baselines/reference/inferContextualTypePredicates1.types
new file mode 100644
index 0000000000000..c2c5d9d06e07f
--- /dev/null
+++ b/tests/baselines/reference/inferContextualTypePredicates1.types
@@ -0,0 +1,290 @@
+//// [tests/cases/compiler/inferContextualTypePredicates1.ts] ////
+
+=== inferContextualTypePredicates1.ts ===
+type Foo = { type: "foo"; foo: number };
+>Foo : Foo
+> : ^^^
+>type : "foo"
+> : ^^^^^
+>foo : number
+> : ^^^^^^
+
+type Bar = { type: "bar"; bar: string };
+>Bar : Bar
+> : ^^^
+>type : "bar"
+> : ^^^^^
+>bar : string
+> : ^^^^^^
+
+declare function skipIf(
+>skipIf : (as: A[], predicate: (a: A) => a is B) => Exclude[]
+> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+
+ as: A[],
+>as : A[]
+> : ^^^
+
+ predicate: (a: A) => a is B,
+>predicate : (a: A) => a is B
+> : ^ ^^ ^^^^^
+>a : A
+> : ^
+
+): Exclude[];
+
+declare const items: (Foo | Bar)[];
+>items : (Foo | Bar)[]
+> : ^^^^^^^^^^^^^
+
+const r1 = skipIf(items, (item) => item.type === "foo"); // ok
+>r1 : Bar[]
+> : ^^^^^
+>skipIf(items, (item) => item.type === "foo") : Bar[]
+> : ^^^^^
+>skipIf : (as: A[], predicate: (a: A) => a is B) => Exclude[]
+> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+>items : (Foo | Bar)[]
+> : ^^^^^^^^^^^^^
+>(item) => item.type === "foo" : (item: Foo | Bar) => item is Foo
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>item : Foo | Bar
+> : ^^^^^^^^^
+>item.type === "foo" : boolean
+> : ^^^^^^^
+>item.type : "foo" | "bar"
+> : ^^^^^^^^^^^^^
+>item : Foo | Bar
+> : ^^^^^^^^^
+>type : "foo" | "bar"
+> : ^^^^^^^^^^^^^
+>"foo" : "foo"
+> : ^^^^^
+
+const r2 = skipIf(items, (item) => item.type === "foo" || item.type === "bar"); // ok
+>r2 : never[]
+> : ^^^^^^^
+>skipIf(items, (item) => item.type === "foo" || item.type === "bar") : never[]
+> : ^^^^^^^
+>skipIf : (as: A[], predicate: (a: A) => a is B) => Exclude[]
+> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+>items : (Foo | Bar)[]
+> : ^^^^^^^^^^^^^
+>(item) => item.type === "foo" || item.type === "bar" : (item: Foo | Bar) => item is Foo | Bar
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>item : Foo | Bar
+> : ^^^^^^^^^
+>item.type === "foo" || item.type === "bar" : boolean
+> : ^^^^^^^
+>item.type === "foo" : boolean
+> : ^^^^^^^
+>item.type : "foo" | "bar"
+> : ^^^^^^^^^^^^^
+>item : Foo | Bar
+> : ^^^^^^^^^
+>type : "foo" | "bar"
+> : ^^^^^^^^^^^^^
+>"foo" : "foo"
+> : ^^^^^
+>item.type === "bar" : boolean
+> : ^^^^^^^
+>item.type : "bar"
+> : ^^^^^
+>item : Bar
+> : ^^^
+>type : "bar"
+> : ^^^^^
+>"bar" : "bar"
+> : ^^^^^
+
+const r3 = skipIf(items, (item) => false); // ok
+>r3 : (Foo | Bar)[]
+> : ^^^^^^^^^^^^^
+>skipIf(items, (item) => false) : (Foo | Bar)[]
+> : ^^^^^^^^^^^^^
+>skipIf : (as: A[], predicate: (a: A) => a is B) => Exclude[]
+> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+>items : (Foo | Bar)[]
+> : ^^^^^^^^^^^^^
+>(item) => false : (item: Foo | Bar) => item is never
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>item : Foo | Bar
+> : ^^^^^^^^^
+>false : false
+> : ^^^^^
+
+const r4 = skipIf(items, (item) => true); // ok
+>r4 : never[]
+> : ^^^^^^^
+>skipIf(items, (item) => true) : never[]
+> : ^^^^^^^
+>skipIf : (as: A[], predicate: (a: A) => a is B) => Exclude[]
+> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+>items : (Foo | Bar)[]
+> : ^^^^^^^^^^^^^
+>(item) => true : (item: Foo | Bar) => item is Foo | Bar
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>item : Foo | Bar
+> : ^^^^^^^^^
+>true : true
+> : ^^^^
+
+const pred1: (a: string | null, b: string | null) => b is string = (a, b) => typeof b === 'string'; // ok
+>pred1 : (a: string | null, b: string | null) => b is string
+> : ^ ^^ ^^ ^^ ^^^^^
+>a : string | null
+> : ^^^^^^^^^^^^^
+>b : string | null
+> : ^^^^^^^^^^^^^
+>(a, b) => typeof b === 'string' : (a: string | null, b: string | null) => b is string
+> : ^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>a : string | null
+> : ^^^^^^^^^^^^^
+>b : string | null
+> : ^^^^^^^^^^^^^
+>typeof b === 'string' : boolean
+> : ^^^^^^^
+>typeof b : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>b : string | null
+> : ^^^^^^^^^^^^^
+>'string' : "string"
+> : ^^^^^^^^
+
+const pred2: (a: string | null, b: string | null) => b is string = (a, b) => typeof a === 'string'; // error
+>pred2 : (a: string | null, b: string | null) => b is string
+> : ^ ^^ ^^ ^^ ^^^^^
+>a : string | null
+> : ^^^^^^^^^^^^^
+>b : string | null
+> : ^^^^^^^^^^^^^
+>(a, b) => typeof a === 'string' : (a: string | null, b: string | null) => boolean
+> : ^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>a : string | null
+> : ^^^^^^^^^^^^^
+>b : string | null
+> : ^^^^^^^^^^^^^
+>typeof a === 'string' : boolean
+> : ^^^^^^^
+>typeof a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>a : string | null
+> : ^^^^^^^^^^^^^
+>'string' : "string"
+> : ^^^^^^^^
+
+export declare function every(array: readonly T[], callback: (element: T, index: number) => element is U): array is readonly U[];
+>every : { (array: readonly T[], callback: (element: T, index: number) => element is U): array is readonly U[]; (array: readonly T_1[] | undefined, callback: (element: T_1, index: number) => element is U_1): array is readonly U_1[] | undefined; (array: readonly T_1[] | undefined, callback: (element: T_1, index: number) => boolean): boolean; }
+> : ^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^
+>array : readonly T[]
+> : ^^^^^^^^^^^^
+>callback : (element: T, index: number) => element is U
+> : ^ ^^ ^^ ^^ ^^^^^
+>element : T
+> : ^
+>index : number
+> : ^^^^^^
+
+export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => element is U): array is readonly U[] | undefined;
+>every : { (array: readonly T_1[], callback: (element: T_1, index: number) => element is U_1): array is readonly U_1[]; (array: readonly T[] | undefined, callback: (element: T, index: number) => element is U): array is readonly U[] | undefined; (array: readonly T_1[] | undefined, callback: (element: T_1, index: number) => boolean): boolean; }
+> : ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^^^^^^ ^^ ^^ ^^ ^^^ ^^^
+>array : readonly T[] | undefined
+> : ^^^^^^^^^^^^^^^^^^^^^^^^
+>callback : (element: T, index: number) => element is U
+> : ^ ^^ ^^ ^^ ^^^^^
+>element : T
+> : ^
+>index : number
+> : ^^^^^^
+
+export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean;
+>every : { (array: readonly T_1[], callback: (element: T_1, index: number) => element is U): array is readonly U[]; (array: readonly T_1[] | undefined, callback: (element: T_1, index: number) => element is U): array is readonly U[] | undefined; (array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean; }
+> : ^^^^^^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^^^^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^^ ^^^
+>array : readonly T[] | undefined
+> : ^^^^^^^^^^^^^^^^^^^^^^^^
+>callback : (element: T, index: number) => boolean
+> : ^ ^^ ^^ ^^ ^^^^^
+>element : T
+> : ^
+>index : number
+> : ^^^^^^
+
+type Type = {
+>Type : Type
+> : ^^^^
+
+ kind: number;
+>kind : number
+> : ^^^^^^
+
+ flags: number;
+>flags : number
+> : ^^^^^^
+
+};
+
+function testEvery(typeSet: Type[]) {
+>testEvery : (typeSet: Type[]) => 1 | 2 | -1
+> : ^ ^^ ^^^^^^^^^^^^^^^
+>typeSet : Type[]
+> : ^^^^^^
+
+ if (every(typeSet, (t) => t.kind === 1)) {
+>every(typeSet, (t) => t.kind === 1) : boolean
+> : ^^^^^^^
+>every : { (array: readonly T[], callback: (element: T, index: number) => element is U): array is readonly U[]; (array: readonly T[] | undefined, callback: (element: T, index: number) => element is U): array is readonly U[] | undefined; (array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean; }
+> : ^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^^ ^^^
+>typeSet : Type[]
+> : ^^^^^^
+>(t) => t.kind === 1 : (t: Type) => boolean
+> : ^ ^^^^^^^^^^^^^^^^^^
+>t : Type
+> : ^^^^
+>t.kind === 1 : boolean
+> : ^^^^^^^
+>t.kind : number
+> : ^^^^^^
+>t : Type
+> : ^^^^
+>kind : number
+> : ^^^^^^
+>1 : 1
+> : ^
+
+ return 1;
+>1 : 1
+> : ^
+ }
+ if (every(typeSet, (t) => t.kind === 2)) {
+>every(typeSet, (t) => t.kind === 2) : boolean
+> : ^^^^^^^
+>every : { (array: readonly T[], callback: (element: T, index: number) => element is U): array is readonly U[]; (array: readonly T[] | undefined, callback: (element: T, index: number) => element is U): array is readonly U[] | undefined; (array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean; }
+> : ^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^^ ^^^
+>typeSet : Type[]
+> : ^^^^^^
+>(t) => t.kind === 2 : (t: Type) => boolean
+> : ^ ^^^^^^^^^^^^^^^^^^
+>t : Type
+> : ^^^^
+>t.kind === 2 : boolean
+> : ^^^^^^^
+>t.kind : number
+> : ^^^^^^
+>t : Type
+> : ^^^^
+>kind : number
+> : ^^^^^^
+>2 : 2
+> : ^
+
+ return 2;
+>2 : 2
+> : ^
+ }
+ return -1;
+>-1 : -1
+> : ^^
+>1 : 1
+> : ^
+}
+
diff --git a/tests/baselines/reference/targetTypeArgs.types b/tests/baselines/reference/targetTypeArgs.types
index 654f04fe5a913..04e1362086a99 100644
--- a/tests/baselines/reference/targetTypeArgs.types
+++ b/tests/baselines/reference/targetTypeArgs.types
@@ -107,8 +107,8 @@ foo(function(x) { x });
> : ^
>every : { (predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }
> : ^^^ ^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^ ^ ^^^ ^^^ ^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^ ^^^ ^^^
->function(v,i,a) {return true;} : (v: number, i: number, a: number[]) => true
-> : ^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
+>function(v,i,a) {return true;} : (v: number, i: number, a: number[]) => v is number
+> : ^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
>v : number
> : ^^^^^^
>i : number
@@ -129,8 +129,8 @@ foo(function(x) { x });
> : ^^^
>every : { (predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean; }
> : ^^^ ^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^ ^ ^^^ ^^^ ^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^ ^^^ ^^^
->function(v,i,a) {return true;} : (v: string, i: number, a: string[]) => true
-> : ^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
+>function(v,i,a) {return true;} : (v: string, i: number, a: string[]) => v is string
+> : ^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
>v : string
> : ^^^^^^
>i : number
diff --git a/tests/cases/compiler/inferContextualTypePredicates1.ts b/tests/cases/compiler/inferContextualTypePredicates1.ts
new file mode 100644
index 0000000000000..a2ed79d183fe6
--- /dev/null
+++ b/tests/cases/compiler/inferContextualTypePredicates1.ts
@@ -0,0 +1,39 @@
+// @strict: true
+// @noEmit: true
+
+type Foo = { type: "foo"; foo: number };
+type Bar = { type: "bar"; bar: string };
+
+declare function skipIf(
+ as: A[],
+ predicate: (a: A) => a is B,
+): Exclude[];
+
+declare const items: (Foo | Bar)[];
+
+const r1 = skipIf(items, (item) => item.type === "foo"); // ok
+const r2 = skipIf(items, (item) => item.type === "foo" || item.type === "bar"); // ok
+const r3 = skipIf(items, (item) => false); // ok
+const r4 = skipIf(items, (item) => true); // ok
+
+const pred1: (a: string | null, b: string | null) => b is string = (a, b) => typeof b === 'string'; // ok
+const pred2: (a: string | null, b: string | null) => b is string = (a, b) => typeof a === 'string'; // error
+
+export declare function every(array: readonly T[], callback: (element: T, index: number) => element is U): array is readonly U[];
+export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => element is U): array is readonly U[] | undefined;
+export declare function every(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean;
+
+type Type = {
+ kind: number;
+ flags: number;
+};
+
+function testEvery(typeSet: Type[]) {
+ if (every(typeSet, (t) => t.kind === 1)) {
+ return 1;
+ }
+ if (every(typeSet, (t) => t.kind === 2)) {
+ return 2;
+ }
+ return -1;
+}