In C# 9 we can solve just by adding ?
public async Task<T?> GetAsync<T>()
{
    return default;
}
But you need to distinguish nullable value types and nullable ref types in the calling code. To get a nullable ref type as a return value you can call the method with either <T> or <T?>:
SomeClass? c = await GetAsync<SomeClass>(); // return type is SomeClass?
SomeClass? c2 = await GetAsync<SomeClass?>(); // return type is SomeClass?
To get a nullable value type you need to call it with <T?>:
int? i = await GetAsync<int?>(); // return type is int?
int i2 = await GetAsync<int>(); // return type is int
P.S. I wonder how Microsoft explains us why they can't allow unconstrained T? and then just does this in the next C# version :)
Another option is to use a code from the answer for C# 8.
Answer for C# 8
We can't have async Task<T?> GetAsync<T>() since SomeClass? and SomeStruct? are very different. Also default! is not the best option since we can get nullable reference on non-nullable reference type by calling GetAsync<SomeClass>() in the calling code.
Better option is to have two different methods that use the same private method:
public class Storage
{
    ...
    public Task<T?> GetClassAsync<T>() where T : class
    {
        return GetAsync<T?>();
    }
    public Task<T?> GetStructAsync<T>() where T : struct
    {
        return GetAsync<T?>();
    }
    private async Task<T> GetAsync<T>()
    {
        if (condition)
            return default!;
        string json = await GetJsonAsync();
        T result = JsonSerializer.Deserialize<T>(json);
        return result;
    }
}
And usage:
// return type is SomeClass?
SomeClass? classResult = await storage.GetClassAsync<SomeClass>();
// return type is int?
int? structResult = await storage.GetStructAsync<int>();
// warning: Nullability of type argument doesn't match 'class' constraint
SomeClass? classResult = await storage.GetClassAsync<SomeClass?>();
// error: The type 'int?' must be a non-nullable value type
int? structResult2 = await storage.GetStructAsync<int?>();