Compiler throws error because you are trying to return something as null which you already constrainted as notnull, which means you telling compiler that T will never ever be null and the same time you also expecting that T might be become null in the future at some point.
And Here is alternative solution without compiler error and same logic
public static T GetValue<T>(this JObject o, string propertyName) where T : notnull
{
if (o.TryGetValue(propertyName, out JToken jtok))
return jtok.ToObject<T>();
else return default;
}
or here is the same with more elegant syntax
public static T GetValue<T>(this JObject o, string propertyName) where T : notnull
=> o.TryGetValue(propertyName, out JToken jtok) ? jtok.ToObject<T>() : default;
Ask in comments if you don't understand the code
EDIT After 1st comment:
Now(after you deleted notnull
constraint) your code telling compiler that T could be anything(value(notNullable) or reference(nullable) type) so when you are checking jtok
with ?.
you are telling compiler that at some point it should expect null value, which if T will be for example int will be error, because you can't just assign null to int directly
consider this use case of your code int i = sampleObj.GetValue<int>("EXAMPLE");
. In this case if "EXAMPLE" property does not exist you are forcing compiler to assign null to int, but compiler is clever and it's throwing that error to protect you from that error