Editor/SerializedPropertyExtensions.cs
using ExtendedEditor.TypeLibraryFixes;
using Sandbox;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace ExtendedEditor;

public static class SerializedPropertyExtensions // TODO: use one from new editor library
{
    extension(SerializedProperty target)
    {
        public SerializedProperty CreateProxy(Type type)
        {
            return new SerializedPropertyProxy(target, type, target.GetAttributes());
        }

        public SerializedProperty CreateProxy(Type type, params IEnumerable<Attribute> attributes)
        {
            return new SerializedPropertyProxy(target, type, attributes);
        }

        public SerializedProperty CreateProxy(params IEnumerable<Attribute> attributes) =>
            target.CreateProxy(target.PropertyType, attributes);


        public SerializedProperty FixForGenerics()
        {
            if(!IsGenericTypeDefinitionInside(target.PropertyType))
                return target;

            var propertyClassType = target.GetType();
            bool isProperty = propertyClassType.FullName == "Sandbox.Internal.TypeSerializedProperty";
            bool isField = propertyClassType.FullName == "Sandbox.Internal.TypeSerializedField";
            if(!isProperty && !isField)
            {
                Log.Warning($"Can't fix {propertyClassType}");
                return target;
            }

            var typeSerializedObjectField = propertyClassType.GetField("typeSerializedObject", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            if(typeSerializedObjectField is null)
            {
                Log.Warning($"Can't fix, field 'typeSerializedObject' not found.");
                return target;
            }

            var serializedObject = (SerializedObject)typeSerializedObjectField!.GetValue(target)!;

            var objectClassType = serializedObject!.GetType();
            var typeDescriptionField = objectClassType.GetField("TypeDescription", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            if(typeDescriptionField is null)
            {
                Log.Warning($"Can't fix, field 'typeDescriptionField' not found.");
                return target;
            }

            var getTargetObjectMethod = objectClassType.GetMethod("GetTargetObject", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            if(getTargetObjectMethod is null)
            {
                Log.Warning($"Can't fix, method 'getTargetObjectMethod' not found.");
                return target;
            }

            var parent = getTargetObjectMethod!.Invoke(serializedObject, null);

            if(isProperty)
            {
                var property = parent!.GetType()!.GetProperty(target.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!;
                if(property is null)
                {
                    Log.Warning($"Can't fix, property '{target.Name}' not found.");
                    return target;
                }

                var proxy = new SerializedPropertyProxy(target, property!.PropertyType, target.GetAttributes());
                proxy.ProxyValueGet(() => property.GetValue(parent)!);
                proxy.ProxyValueSet(x => property.SetValue(parent, x));

                return proxy;
            }
            else
            {
                var field = parent!.GetType()!.GetField(target.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!;
                if(field is null)
                {
                    Log.Warning($"Can't fix, field '{target.Name}' not found.");
                    return target;
                }

                var proxy = new SerializedPropertyProxy(target, field!.FieldType, target.GetAttributes());
                proxy.ProxyValueGet(() => field.GetValue(parent)!);
                proxy.ProxyValueSet(x => field.SetValue(parent, x));

                return proxy;
            }
        }

        static bool IsGenericTypeDefinitionInside(Type type)
        {
            if(type.IsGenericParameter)
                return true;

            if(type.IsGenericTypeDefinition)
                return true;

            if(type.IsGenericType)
            {
                foreach(var arg in type.GetGenericArguments())
                {
                    if(arg.IsGenericParameter)
                        return true;

                    if(IsGenericTypeDefinitionInside(arg))
                        return true;
                }
            }

            return false;
        }
    }
}