diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp
index 33c285dc6d0e..aed83ac010d6 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -900,6 +900,39 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorNotInt>(Variant::OP_NOT, Variant::INT, Variant::NIL);
 	register_op<OperatorEvaluatorNotFloat>(Variant::OP_NOT, Variant::FLOAT, Variant::NIL);
 	register_op<OperatorEvaluatorNotObject>(Variant::OP_NOT, Variant::OBJECT, Variant::NIL);
+	register_op<OperatorEvaluatorNot<String>>(Variant::OP_NOT, Variant::STRING, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Vector2>>(Variant::OP_NOT, Variant::VECTOR2, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Vector2i>>(Variant::OP_NOT, Variant::VECTOR2I, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Rect2>>(Variant::OP_NOT, Variant::RECT2, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Rect2i>>(Variant::OP_NOT, Variant::RECT2I, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Vector3>>(Variant::OP_NOT, Variant::VECTOR3, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Vector3i>>(Variant::OP_NOT, Variant::VECTOR3I, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Transform2D>>(Variant::OP_NOT, Variant::TRANSFORM2D, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Vector4>>(Variant::OP_NOT, Variant::VECTOR4, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Vector4i>>(Variant::OP_NOT, Variant::VECTOR4I, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Plane>>(Variant::OP_NOT, Variant::PLANE, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Quaternion>>(Variant::OP_NOT, Variant::QUATERNION, Variant::NIL);
+	register_op<OperatorEvaluatorNot<::AABB>>(Variant::OP_NOT, Variant::AABB, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Basis>>(Variant::OP_NOT, Variant::BASIS, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Transform3D>>(Variant::OP_NOT, Variant::TRANSFORM3D, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Projection>>(Variant::OP_NOT, Variant::PROJECTION, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Color>>(Variant::OP_NOT, Variant::COLOR, Variant::NIL);
+	register_op<OperatorEvaluatorNot<StringName>>(Variant::OP_NOT, Variant::STRING_NAME, Variant::NIL);
+	register_op<OperatorEvaluatorNot<NodePath>>(Variant::OP_NOT, Variant::NODE_PATH, Variant::NIL);
+	register_op<OperatorEvaluatorNot<::RID>>(Variant::OP_NOT, Variant::RID, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Callable>>(Variant::OP_NOT, Variant::CALLABLE, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Signal>>(Variant::OP_NOT, Variant::SIGNAL, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Dictionary>>(Variant::OP_NOT, Variant::DICTIONARY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<Array>>(Variant::OP_NOT, Variant::ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedByteArray>>(Variant::OP_NOT, Variant::PACKED_BYTE_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedInt32Array>>(Variant::OP_NOT, Variant::PACKED_INT32_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedInt64Array>>(Variant::OP_NOT, Variant::PACKED_INT64_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedFloat32Array>>(Variant::OP_NOT, Variant::PACKED_FLOAT32_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedFloat64Array>>(Variant::OP_NOT, Variant::PACKED_FLOAT64_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedStringArray>>(Variant::OP_NOT, Variant::PACKED_STRING_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedVector2Array>>(Variant::OP_NOT, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedVector3Array>>(Variant::OP_NOT, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedColorArray>>(Variant::OP_NOT, Variant::PACKED_COLOR_ARRAY, Variant::NIL);
 
 	register_string_op(OperatorEvaluatorInStringFind, Variant::OP_IN);
 
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index d2163cf92dd6..5db4d6380676 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -805,14 +805,14 @@ class OperatorEvaluatorNot {
 public:
 	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
 		const A &a = *VariantGetInternalPtr<A>::get_ptr(&p_left);
-		*r_ret = !a;
+		*r_ret = a == A();
 		r_valid = true;
 	}
 	static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
-		*VariantGetInternalPtr<bool>::get_ptr(r_ret) = !*VariantGetInternalPtr<A>::get_ptr(left);
+		*VariantGetInternalPtr<bool>::get_ptr(r_ret) = *VariantGetInternalPtr<A>::get_ptr(left) == A();
 	}
 	static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
-		PtrToArg<bool>::encode(!PtrToArg<A>::convert(left));
+		PtrToArg<bool>::encode(PtrToArg<A>::convert(left) == A(), r_ret);
 	}
 	static Variant::Type get_return_type() { return Variant::BOOL; }
 };
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 38d5ae6b77cf..831f9a3efd0a 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -4846,6 +4846,17 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
 }
 
 GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
+	if (p_operation == Variant::OP_AND || p_operation == Variant::OP_OR) {
+		// Those work for any type of argument and always return a boolean.
+		// They don't use the Variant operator since they have short-circuit semantics.
+		r_valid = true;
+		GDScriptParser::DataType result;
+		result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+		result.kind = GDScriptParser::DataType::BUILTIN;
+		result.builtin_type = Variant::BOOL;
+		return result;
+	}
+
 	Variant::Type a_type = p_a.builtin_type;
 	Variant::Type b_type = p_b.builtin_type;
 
diff --git a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd
new file mode 100644
index 000000000000..73d0f9096c67
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd
@@ -0,0 +1,347 @@
+extends Resource
+
+signal foo
+
+func test():
+	var x
+	# TYPE_NIL
+	x = null
+	prints("TYPE_NIL")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_BOOL
+	x = true
+	prints("TYPE_BOOL")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_INT
+	x = 1
+	prints("TYPE_INT")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_FLOAT
+	x = 1.1
+	prints("TYPE_FLOAT")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_STRING
+	x = "foo"
+	prints("TYPE_STRING")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_VECTOR2
+	x = Vector2(1, 1)
+	prints("TYPE_VECTOR2")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_VECTOR2I
+	x = Vector2i(1, 1)
+	prints("TYPE_VECTOR2I")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_RECT2
+	x = Rect2(1, 1, 1, 1)
+	prints("TYPE_RECT2")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_RECT2I
+	x = Rect2i(1, 1, 1, 1)
+	prints("TYPE_RECT2I")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_VECTOR3
+	x = Vector3(1, 1, 1)
+	prints("TYPE_VECTOR3")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_VECTOR3I
+	x = Vector3i(1, 1, 1)
+	prints("TYPE_VECTOR3I")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_TRANSFORM2D
+	x = Transform2D.IDENTITY
+	prints("TYPE_TRANSFORM2D")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_VECTOR4
+	x = Vector4(1, 1, 1, 1)
+	prints("TYPE_VECTOR4")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_VECTOR4I
+	x = Vector4i(1, 1, 1, 1)
+	prints("TYPE_VECTOR4I")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PLANE
+	x = Plane.PLANE_XY
+	prints("TYPE_PLANE")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_QUATERNION
+	x = Quaternion.IDENTITY
+	prints("TYPE_QUATERNION")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_AABB
+	x = AABB(Vector3.ONE, Vector3.ONE)
+	prints("TYPE_AABB")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_BASIS
+	x = Basis.IDENTITY
+	prints("TYPE_BASIS")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_TRANSFORM3D
+	x = Transform3D.IDENTITY
+	prints("TYPE_TRANSFORM3D")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PROJECTION
+	x = Projection.IDENTITY
+	prints("TYPE_PROJECTION")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_COLOR
+	x = Color.WHITE
+	prints("TYPE_COLOR")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_STRING_NAME
+	x = &"name"
+	prints("TYPE_STRING_NAME")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_NODE_PATH
+	x = ^"path"
+	prints("TYPE_NODE_PATH")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_RID
+	x = get_rid()
+	prints("TYPE_RID")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_OBJECT
+	x = self
+	prints("TYPE_OBJECT")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_CALLABLE
+	x = test
+	prints("TYPE_CALLABLE")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_SIGNAL
+	x = foo
+	prints("TYPE_SIGNAL")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_DICTIONARY
+	x = { a = 1}
+	prints("TYPE_DICTIONARY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_ARRAY
+	x = [1]
+	prints("TYPE_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_BYTE_ARRAY
+	x = PackedByteArray([1])
+	prints("TYPE_PACKED_BYTE_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_INT32_ARRAY
+	x = PackedInt32Array([1])
+	prints("TYPE_PACKED_INT32_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_INT64_ARRAY
+	x = PackedInt64Array([1])
+	prints("TYPE_PACKED_INT64_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_FLOAT32_ARRAY
+	x = PackedFloat32Array([1])
+	prints("TYPE_PACKED_FLOAT32_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_FLOAT64_ARRAY
+	x = PackedFloat64Array([1])
+	prints("TYPE_PACKED_FLOAT64_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_STRING_ARRAY
+	x = PackedStringArray(["1"])
+	prints("TYPE_PACKED_STRING_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_VECTOR2_ARRAY
+	x = PackedVector2Array([Vector2.ONE])
+	prints("TYPE_PACKED_VECTOR2_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_VECTOR3_ARRAY
+	x = PackedVector3Array([Vector3.ONE])
+	prints("TYPE_PACKED_VECTOR3_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
+
+	# TYPE_PACKED_COLOR_ARRAY
+	x = PackedColorArray([Color.WHITE])
+	prints("TYPE_PACKED_COLOR_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out
new file mode 100644
index 000000000000..e2945c910aed
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out
@@ -0,0 +1,229 @@
+GDTEST_OK
+TYPE_NIL
+true
+false
+false
+false
+true
+TYPE_BOOL
+false
+false
+true
+true
+true
+TYPE_INT
+false
+false
+true
+true
+true
+TYPE_FLOAT
+false
+false
+true
+true
+true
+TYPE_STRING
+false
+false
+true
+true
+true
+TYPE_VECTOR2
+false
+false
+true
+true
+true
+TYPE_VECTOR2I
+false
+false
+true
+true
+true
+TYPE_RECT2
+false
+false
+true
+true
+true
+TYPE_RECT2I
+false
+false
+true
+true
+true
+TYPE_VECTOR3
+false
+false
+true
+true
+true
+TYPE_VECTOR3I
+false
+false
+true
+true
+true
+TYPE_TRANSFORM2D
+true
+false
+false
+false
+true
+TYPE_VECTOR4
+false
+false
+true
+true
+true
+TYPE_VECTOR4I
+false
+false
+true
+true
+true
+TYPE_PLANE
+false
+false
+true
+true
+true
+TYPE_QUATERNION
+true
+false
+false
+false
+true
+TYPE_AABB
+false
+false
+true
+true
+true
+TYPE_BASIS
+true
+false
+false
+false
+true
+TYPE_TRANSFORM3D
+true
+false
+false
+false
+true
+TYPE_PROJECTION
+true
+false
+false
+false
+true
+TYPE_COLOR
+false
+false
+true
+true
+true
+TYPE_STRING_NAME
+false
+false
+true
+true
+true
+TYPE_NODE_PATH
+false
+false
+true
+true
+true
+TYPE_RID
+true
+false
+false
+false
+true
+TYPE_OBJECT
+false
+false
+true
+true
+true
+TYPE_CALLABLE
+false
+false
+true
+true
+true
+TYPE_SIGNAL
+false
+false
+true
+true
+true
+TYPE_DICTIONARY
+false
+false
+true
+true
+true
+TYPE_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_BYTE_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_INT32_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_INT64_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_FLOAT32_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_FLOAT64_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_STRING_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_VECTOR2_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_VECTOR3_ARRAY
+false
+false
+true
+true
+true
+TYPE_PACKED_COLOR_ARRAY
+false
+false
+true
+true
+true
diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h
index 024fcf53c437..66b58620d281 100644
--- a/tests/core/variant/test_variant.h
+++ b/tests/core/variant/test_variant.h
@@ -1171,6 +1171,22 @@ TEST_CASE("[Variant] Utility functions") {
 	}
 }
 
+TEST_CASE("[Variant] Operator NOT") {
+	// Verify that operator NOT works for all types and is consistent with booleanize().
+	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+		Variant value;
+		Callable::CallError err;
+		Variant::construct((Variant::Type)i, value, nullptr, 0, err);
+
+		REQUIRE_EQ(err.error, Callable::CallError::CALL_OK);
+
+		Variant result = Variant::evaluate(Variant::OP_NOT, value, Variant());
+
+		REQUIRE_EQ(result.get_type(), Variant::BOOL);
+		CHECK_EQ(!value.booleanize(), result.operator bool());
+	}
+}
+
 } // namespace TestVariant
 
 #endif // TEST_VARIANT_H