diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 1f2d87de5da94..68237162a1595 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -16486,36 +16486,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
     }
 
     function getUnionOrIntersectionTypePredicate(signatures: readonly Signature[], kind: TypeFlags | undefined): TypePredicate | undefined {
-        let first: TypePredicate | undefined;
+        let last: TypePredicate | undefined;
         const types: Type[] = [];
         for (const sig of signatures) {
             const pred = getTypePredicateOfSignature(sig);
-            if (!pred || pred.kind === TypePredicateKind.AssertsThis || pred.kind === TypePredicateKind.AssertsIdentifier) {
-                if (kind !== TypeFlags.Intersection) {
-                    continue;
-                }
-                else {
-                    return; // intersections demand all members be type predicates for the result to have a predicate
-                }
-            }
-
-            if (first) {
-                if (!typePredicateKindsMatch(first, pred)) {
-                    // No common type predicate.
+            if (pred) {
+                // Constituent type predicates must all have matching kinds. We don't create composite type predicates for assertions.
+                if (pred.kind !== TypePredicateKind.This && pred.kind !== TypePredicateKind.Identifier || last && !typePredicateKindsMatch(last, pred)) {
                     return undefined;
                 }
+                last = pred;
+                types.push(pred.type);
             }
             else {
-                first = pred;
+                // In composite union signatures we permit and ignore signatures with a return type `false`.
+                const returnType = kind !== TypeFlags.Intersection ? getReturnTypeOfSignature(sig) : undefined;
+                if (returnType !== falseType && returnType !== regularFalseType) {
+                    return undefined;
+                }
             }
-            types.push(pred.type);
         }
-        if (!first) {
-            // No signatures had a type predicate.
+        if (!last) {
             return undefined;
         }
         const compositeType = getUnionOrIntersectionType(types, kind);
-        return createTypePredicate(first.kind, first.parameterName, first.parameterIndex, compositeType);
+        return createTypePredicate(last.kind, last.parameterName, last.parameterIndex, compositeType);
     }
 
     function typePredicateKindsMatch(a: TypePredicate, b: TypePredicate): boolean {
diff --git a/tests/baselines/reference/typePredicatesInUnion3.errors.txt b/tests/baselines/reference/typePredicatesInUnion3.errors.txt
new file mode 100644
index 0000000000000..8f30ea560097a
--- /dev/null
+++ b/tests/baselines/reference/typePredicatesInUnion3.errors.txt
@@ -0,0 +1,69 @@
+tests/cases/compiler/typePredicatesInUnion3.ts(59,24): error TS2345: Argument of type 'number | null' is not assignable to parameter of type 'number'.
+  Type 'null' is not assignable to type 'number'.
+
+
+==== tests/cases/compiler/typePredicatesInUnion3.ts (1 errors) ====
+    // A union of function types is considered a type predicate if at least one constituent is a type
+    // predicate and the other constituents are matching type predicates or functions returning `false`.
+    
+    type P1 = (x: unknown) => x is string;
+    type P2 = (x: unknown) => x is number;
+    
+    type F1 = (x: unknown) => false;
+    type F2 = (x: unknown) => boolean;
+    type F3 = (x: unknown) => string;
+    
+    function f1(x: unknown, p: P1 | P2) {
+        if (p(x)) {
+            x;  // string | number
+        }
+    }
+    
+    function f2(x: unknown, p: P1 | P2 | F1) {
+        if (p(x)) {
+            x;  // string | number
+        }
+    }
+    
+    function f3(x: unknown, p: P1 | P2 | F2) {
+        if (p(x)) {
+            x;  // unknown
+        }
+    }
+    
+    function f4(x: unknown, p: P1 | P2 | F3) {
+        if (p(x)) {
+            x;  // unknown
+        }
+    }
+    
+    // Repro from #54143
+    
+    type HasAttribute<T> = T & { attribute: number };
+    
+    class Type1 {
+        attribute: number | null = null;
+        predicate(): this is HasAttribute<Type1> {
+            return true;
+        }
+    }
+    
+    class Type2 {
+        attribute: number | null = null;
+        predicate(): boolean {
+            return true;
+        }
+    }
+    
+    function assertType<T>(_val: T) {
+    }
+    
+    declare const val: Type1 | Type2;
+    
+    if (val.predicate()) {
+        assertType<number>(val.attribute);  // Error
+                           ~~~~~~~~~~~~~
+!!! error TS2345: Argument of type 'number | null' is not assignable to parameter of type 'number'.
+!!! error TS2345:   Type 'null' is not assignable to type 'number'.
+    }
+    
\ No newline at end of file
diff --git a/tests/baselines/reference/typePredicatesInUnion3.symbols b/tests/baselines/reference/typePredicatesInUnion3.symbols
new file mode 100644
index 0000000000000..55a214ac270ee
--- /dev/null
+++ b/tests/baselines/reference/typePredicatesInUnion3.symbols
@@ -0,0 +1,153 @@
+=== tests/cases/compiler/typePredicatesInUnion3.ts ===
+// A union of function types is considered a type predicate if at least one constituent is a type
+// predicate and the other constituents are matching type predicates or functions returning `false`.
+
+type P1 = (x: unknown) => x is string;
+>P1 : Symbol(P1, Decl(typePredicatesInUnion3.ts, 0, 0))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 3, 11))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 3, 11))
+
+type P2 = (x: unknown) => x is number;
+>P2 : Symbol(P2, Decl(typePredicatesInUnion3.ts, 3, 38))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 4, 11))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 4, 11))
+
+type F1 = (x: unknown) => false;
+>F1 : Symbol(F1, Decl(typePredicatesInUnion3.ts, 4, 38))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 6, 11))
+
+type F2 = (x: unknown) => boolean;
+>F2 : Symbol(F2, Decl(typePredicatesInUnion3.ts, 6, 32))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 7, 11))
+
+type F3 = (x: unknown) => string;
+>F3 : Symbol(F3, Decl(typePredicatesInUnion3.ts, 7, 34))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 8, 11))
+
+function f1(x: unknown, p: P1 | P2) {
+>f1 : Symbol(f1, Decl(typePredicatesInUnion3.ts, 8, 33))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 10, 12))
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 10, 23))
+>P1 : Symbol(P1, Decl(typePredicatesInUnion3.ts, 0, 0))
+>P2 : Symbol(P2, Decl(typePredicatesInUnion3.ts, 3, 38))
+
+    if (p(x)) {
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 10, 23))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 10, 12))
+
+        x;  // string | number
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 10, 12))
+    }
+}
+
+function f2(x: unknown, p: P1 | P2 | F1) {
+>f2 : Symbol(f2, Decl(typePredicatesInUnion3.ts, 14, 1))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 16, 12))
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 16, 23))
+>P1 : Symbol(P1, Decl(typePredicatesInUnion3.ts, 0, 0))
+>P2 : Symbol(P2, Decl(typePredicatesInUnion3.ts, 3, 38))
+>F1 : Symbol(F1, Decl(typePredicatesInUnion3.ts, 4, 38))
+
+    if (p(x)) {
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 16, 23))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 16, 12))
+
+        x;  // string | number
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 16, 12))
+    }
+}
+
+function f3(x: unknown, p: P1 | P2 | F2) {
+>f3 : Symbol(f3, Decl(typePredicatesInUnion3.ts, 20, 1))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 22, 12))
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 22, 23))
+>P1 : Symbol(P1, Decl(typePredicatesInUnion3.ts, 0, 0))
+>P2 : Symbol(P2, Decl(typePredicatesInUnion3.ts, 3, 38))
+>F2 : Symbol(F2, Decl(typePredicatesInUnion3.ts, 6, 32))
+
+    if (p(x)) {
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 22, 23))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 22, 12))
+
+        x;  // unknown
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 22, 12))
+    }
+}
+
+function f4(x: unknown, p: P1 | P2 | F3) {
+>f4 : Symbol(f4, Decl(typePredicatesInUnion3.ts, 26, 1))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 28, 12))
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 28, 23))
+>P1 : Symbol(P1, Decl(typePredicatesInUnion3.ts, 0, 0))
+>P2 : Symbol(P2, Decl(typePredicatesInUnion3.ts, 3, 38))
+>F3 : Symbol(F3, Decl(typePredicatesInUnion3.ts, 7, 34))
+
+    if (p(x)) {
+>p : Symbol(p, Decl(typePredicatesInUnion3.ts, 28, 23))
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 28, 12))
+
+        x;  // unknown
+>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 28, 12))
+    }
+}
+
+// Repro from #54143
+
+type HasAttribute<T> = T & { attribute: number };
+>HasAttribute : Symbol(HasAttribute, Decl(typePredicatesInUnion3.ts, 32, 1))
+>T : Symbol(T, Decl(typePredicatesInUnion3.ts, 36, 18))
+>T : Symbol(T, Decl(typePredicatesInUnion3.ts, 36, 18))
+>attribute : Symbol(attribute, Decl(typePredicatesInUnion3.ts, 36, 28))
+
+class Type1 {
+>Type1 : Symbol(Type1, Decl(typePredicatesInUnion3.ts, 36, 49))
+
+    attribute: number | null = null;
+>attribute : Symbol(Type1.attribute, Decl(typePredicatesInUnion3.ts, 38, 13))
+
+    predicate(): this is HasAttribute<Type1> {
+>predicate : Symbol(Type1.predicate, Decl(typePredicatesInUnion3.ts, 39, 36))
+>HasAttribute : Symbol(HasAttribute, Decl(typePredicatesInUnion3.ts, 32, 1))
+>Type1 : Symbol(Type1, Decl(typePredicatesInUnion3.ts, 36, 49))
+
+        return true;
+    }
+}
+
+class Type2 {
+>Type2 : Symbol(Type2, Decl(typePredicatesInUnion3.ts, 43, 1))
+
+    attribute: number | null = null;
+>attribute : Symbol(Type2.attribute, Decl(typePredicatesInUnion3.ts, 45, 13))
+
+    predicate(): boolean {
+>predicate : Symbol(Type2.predicate, Decl(typePredicatesInUnion3.ts, 46, 36))
+
+        return true;
+    }
+}
+
+function assertType<T>(_val: T) {
+>assertType : Symbol(assertType, Decl(typePredicatesInUnion3.ts, 50, 1))
+>T : Symbol(T, Decl(typePredicatesInUnion3.ts, 52, 20))
+>_val : Symbol(_val, Decl(typePredicatesInUnion3.ts, 52, 23))
+>T : Symbol(T, Decl(typePredicatesInUnion3.ts, 52, 20))
+}
+
+declare const val: Type1 | Type2;
+>val : Symbol(val, Decl(typePredicatesInUnion3.ts, 55, 13))
+>Type1 : Symbol(Type1, Decl(typePredicatesInUnion3.ts, 36, 49))
+>Type2 : Symbol(Type2, Decl(typePredicatesInUnion3.ts, 43, 1))
+
+if (val.predicate()) {
+>val.predicate : Symbol(predicate, Decl(typePredicatesInUnion3.ts, 39, 36), Decl(typePredicatesInUnion3.ts, 46, 36))
+>val : Symbol(val, Decl(typePredicatesInUnion3.ts, 55, 13))
+>predicate : Symbol(predicate, Decl(typePredicatesInUnion3.ts, 39, 36), Decl(typePredicatesInUnion3.ts, 46, 36))
+
+    assertType<number>(val.attribute);  // Error
+>assertType : Symbol(assertType, Decl(typePredicatesInUnion3.ts, 50, 1))
+>val.attribute : Symbol(attribute, Decl(typePredicatesInUnion3.ts, 38, 13), Decl(typePredicatesInUnion3.ts, 45, 13))
+>val : Symbol(val, Decl(typePredicatesInUnion3.ts, 55, 13))
+>attribute : Symbol(attribute, Decl(typePredicatesInUnion3.ts, 38, 13), Decl(typePredicatesInUnion3.ts, 45, 13))
+}
+
diff --git a/tests/baselines/reference/typePredicatesInUnion3.types b/tests/baselines/reference/typePredicatesInUnion3.types
new file mode 100644
index 0000000000000..7071e9ab4161d
--- /dev/null
+++ b/tests/baselines/reference/typePredicatesInUnion3.types
@@ -0,0 +1,141 @@
+=== tests/cases/compiler/typePredicatesInUnion3.ts ===
+// A union of function types is considered a type predicate if at least one constituent is a type
+// predicate and the other constituents are matching type predicates or functions returning `false`.
+
+type P1 = (x: unknown) => x is string;
+>P1 : (x: unknown) => x is string
+>x : unknown
+
+type P2 = (x: unknown) => x is number;
+>P2 : (x: unknown) => x is number
+>x : unknown
+
+type F1 = (x: unknown) => false;
+>F1 : (x: unknown) => false
+>x : unknown
+>false : false
+
+type F2 = (x: unknown) => boolean;
+>F2 : (x: unknown) => boolean
+>x : unknown
+
+type F3 = (x: unknown) => string;
+>F3 : (x: unknown) => string
+>x : unknown
+
+function f1(x: unknown, p: P1 | P2) {
+>f1 : (x: unknown, p: P1 | P2) => void
+>x : unknown
+>p : P1 | P2
+
+    if (p(x)) {
+>p(x) : boolean
+>p : P1 | P2
+>x : unknown
+
+        x;  // string | number
+>x : string | number
+    }
+}
+
+function f2(x: unknown, p: P1 | P2 | F1) {
+>f2 : (x: unknown, p: P1 | P2 | F1) => void
+>x : unknown
+>p : P1 | P2 | F1
+
+    if (p(x)) {
+>p(x) : boolean
+>p : P1 | P2 | F1
+>x : unknown
+
+        x;  // string | number
+>x : string | number
+    }
+}
+
+function f3(x: unknown, p: P1 | P2 | F2) {
+>f3 : (x: unknown, p: P1 | P2 | F2) => void
+>x : unknown
+>p : P1 | P2 | F2
+
+    if (p(x)) {
+>p(x) : boolean
+>p : P1 | P2 | F2
+>x : unknown
+
+        x;  // unknown
+>x : unknown
+    }
+}
+
+function f4(x: unknown, p: P1 | P2 | F3) {
+>f4 : (x: unknown, p: P1 | P2 | F3) => void
+>x : unknown
+>p : P1 | P2 | F3
+
+    if (p(x)) {
+>p(x) : string | boolean
+>p : P1 | P2 | F3
+>x : unknown
+
+        x;  // unknown
+>x : unknown
+    }
+}
+
+// Repro from #54143
+
+type HasAttribute<T> = T & { attribute: number };
+>HasAttribute : HasAttribute<T>
+>attribute : number
+
+class Type1 {
+>Type1 : Type1
+
+    attribute: number | null = null;
+>attribute : number | null
+
+    predicate(): this is HasAttribute<Type1> {
+>predicate : () => this is HasAttribute<Type1>
+
+        return true;
+>true : true
+    }
+}
+
+class Type2 {
+>Type2 : Type2
+
+    attribute: number | null = null;
+>attribute : number | null
+
+    predicate(): boolean {
+>predicate : () => boolean
+
+        return true;
+>true : true
+    }
+}
+
+function assertType<T>(_val: T) {
+>assertType : <T>(_val: T) => void
+>_val : T
+}
+
+declare const val: Type1 | Type2;
+>val : Type1 | Type2
+
+if (val.predicate()) {
+>val.predicate() : boolean
+>val.predicate : (() => this is HasAttribute<Type1>) | (() => boolean)
+>val : Type1 | Type2
+>predicate : (() => this is HasAttribute<Type1>) | (() => boolean)
+
+    assertType<number>(val.attribute);  // Error
+>assertType<number>(val.attribute) : void
+>assertType : <T>(_val: T) => void
+>val.attribute : number | null
+>val : Type1 | Type2
+>attribute : number | null
+}
+
diff --git a/tests/cases/compiler/typePredicatesInUnion3.ts b/tests/cases/compiler/typePredicatesInUnion3.ts
new file mode 100644
index 0000000000000..f6344cf9bf17a
--- /dev/null
+++ b/tests/cases/compiler/typePredicatesInUnion3.ts
@@ -0,0 +1,63 @@
+// @strict: true
+// @noEmit: true
+
+// A union of function types is considered a type predicate if at least one constituent is a type
+// predicate and the other constituents are matching type predicates or functions returning `false`.
+
+type P1 = (x: unknown) => x is string;
+type P2 = (x: unknown) => x is number;
+
+type F1 = (x: unknown) => false;
+type F2 = (x: unknown) => boolean;
+type F3 = (x: unknown) => string;
+
+function f1(x: unknown, p: P1 | P2) {
+    if (p(x)) {
+        x;  // string | number
+    }
+}
+
+function f2(x: unknown, p: P1 | P2 | F1) {
+    if (p(x)) {
+        x;  // string | number
+    }
+}
+
+function f3(x: unknown, p: P1 | P2 | F2) {
+    if (p(x)) {
+        x;  // unknown
+    }
+}
+
+function f4(x: unknown, p: P1 | P2 | F3) {
+    if (p(x)) {
+        x;  // unknown
+    }
+}
+
+// Repro from #54143
+
+type HasAttribute<T> = T & { attribute: number };
+
+class Type1 {
+    attribute: number | null = null;
+    predicate(): this is HasAttribute<Type1> {
+        return true;
+    }
+}
+
+class Type2 {
+    attribute: number | null = null;
+    predicate(): boolean {
+        return true;
+    }
+}
+
+function assertType<T>(_val: T) {
+}
+
+declare const val: Type1 | Type2;
+
+if (val.predicate()) {
+    assertType<number>(val.attribute);  // Error
+}