I have an abstract class, call it FooClass. And there are two classes that inherit from it, call 'em BazClass and DazClass. I want to create instances of BazClass and DazClass at runtime.

I have scriptable object which I want to store a variable referring to the type of either BazClass or DazClass (Not to an instance of BazClass or DazClass), so I know which type to instantiate at runtime.

  1. Can I have a variable that takes types inherited from another class?

    For example:

    using System;
    Type<:T> variableName where T is FooClass; 
    //stores a type that inherits from FooClass.
  2. How can I visualise this variable in the Unity inspector?

  • \$\begingroup\$ Have you tried something like public class MyType<T> : System.Type where T : FooClass ? \$\endgroup\$
    – Mangata
    Nov 20, 2022 at 11:16

1 Answer 1


Here's a demo of this working with a "Type Store" scriptable object storing two types: FooDerivedA which is a subclass of FooBase, and MeshCollider which is a subclass of Collider (so you can see it works with built-in types too)

Inspector screenshot with drop-down menu to select collider subtype

First, we can define a class to store our serialized type information. System.Type isn't itself serializable, so we'll need to store an identifier like its assembly-qualified name, and use that to look up the correct type when deserializing:

using UnityEngine;

public class InspectableType<T> : ISerializationCallbackReceiver {

    [SerializeField] string qualifiedName;

    System.Type storedType;

    // HACK: I wasn't able to find the base type from the SerializedProperty,
    // so I'm smuggling it in via an extra string stored only in-editor.
    [SerializeField] string baseTypeName;

    public InspectableType(System.Type typeToStore) {
        storedType = typeToStore;

    public override string ToString() {
        if (storedType == null) return string.Empty;
        return storedType.Name;

    public void OnBeforeSerialize() {
        qualifiedName = storedType.AssemblyQualifiedName;

        #if UNITY_EDITOR
        baseTypeName = typeof(T).AssemblyQualifiedName;

    public void OnAfterDeserialize() {
        if (string.IsNullOrEmpty(qualifiedName) || qualifiedName == "null") {
            storedType = null;
        storedType = System.Type.GetType(qualifiedName);

    public static implicit operator System.Type(InspectableType<T> t) => t.storedType;

    // TODO: Validate that t is a subtype of T?
    public static implicit operator InspectableType<T>(System.Type t) => new InspectableType<T>(t);

This lets you add a field in your scriptable object something like this:

InspectableType<BaseTypeOrInterface> myType = typeof(DerivedType);

I've implemented implicit conversions to and from System.Type so you can mostly ignore this wrapper elsewhere.

Next, create a folder called "Editor" and add the following property drawer:

using UnityEngine;
using UnityEditor;
using System.Linq;

using Type = System.Type;

[CustomPropertyDrawer(typeof(InspectableType<>), true)]
public class InspectableTypeDrawer : PropertyDrawer {
    System.Type[] _derivedTypes;
    GUIContent[] _optionLabels;
    int _selectedIndex;

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        var storedProperty = property.FindPropertyRelative("qualifiedName");
        string qualifiedName = storedProperty.stringValue;

        if (_optionLabels == null) {
            Initialize(property, storedProperty);
        } else if (_selectedIndex == _derivedTypes.Length) {
            if (qualifiedName != "null") UpdateIndex(storedProperty);
        } else {
            if (qualifiedName != _derivedTypes[_selectedIndex].AssemblyQualifiedName) UpdateIndex(storedProperty);

        var propLabel = EditorGUI.BeginProperty(position, label, property);

        _selectedIndex = EditorGUI.Popup(position, propLabel, _selectedIndex, _optionLabels);

        if (EditorGUI.EndChangeCheck()) {            
            storedProperty.stringValue = _selectedIndex < _derivedTypes.Length ? _derivedTypes[_selectedIndex].AssemblyQualifiedName : "null";

    static Type[] FindAllDerivedTypes(Type baseType)
        return baseType.Assembly
            .Where(t =>
                t != baseType &&

    void Initialize(SerializedProperty property, SerializedProperty stored) {       

        var baseTypeProperty = property.FindPropertyRelative("baseTypeName");
        var baseType = Type.GetType(baseTypeProperty.stringValue);

        _derivedTypes = FindAllDerivedTypes(baseType);

        if (_derivedTypes.Length == 0) {
            _optionLabels = new[] { new GUIContent($"No types derived from {baseType.Name} found.")};
        _optionLabels = new GUIContent[_derivedTypes.Length+1];
        for (int i = 0; i < _derivedTypes.Length; i++) {
            _optionLabels[i] = new GUIContent(_derivedTypes[i].Name);
        _optionLabels[_derivedTypes.Length] = new GUIContent("null");


    void UpdateIndex(SerializedProperty stored) {
        string qualifiedName = stored.stringValue;
        for (int i = 0; i < _derivedTypes.Length; i++)
            if (_derivedTypes[i].AssemblyQualifiedName == qualifiedName)
                _selectedIndex = i;
        _selectedIndex = _derivedTypes.Length;
        stored.stringValue = "null";


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .