Generic Return Type Based on Class

How do I make the method return type generic?

You could define callFriend this way:

public <T extends Animal> T callFriend(String name, Class<T> type) {
return type.cast(friends.get(name));
}

Then call it as such:

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

This code has the benefit of not generating any compiler warnings. Of course this is really just an updated version of casting from the pre-generic days and doesn't add any additional safety.

In Typescript how to get the generic return type of a method on a class instantiated in a factory(ish) function

This answer was inspired by @jcalz's suggestion in the comments. I wasn't able to make it work using infer, as he suggested, but his suggestion using ReturnType works as intended and this is what I use here.

interface Foo<T> {
foo(): T;
}

class Bar implements Foo<string> {
foo(): string {
return 'Bar';
}
}

class Baz implements Foo<number> {
foo(): number {
return 43;
}
}

class FooRef<T extends Foo<U>, U = ReturnType<T["foo"]>> {
constructor(public instance: T) { }

doIt(): U {
return this.instance.foo();
}
}

function fooBar<T extends Foo<any>>(foo: new (...args: any[]) => T) {
return new FooRef(new foo());
}

const barRef = fooBar(Bar);
barRef.instance // (property) FooRef<Bar, string>.instance: Bar
console.log(barRef.doIt().toUpperCase()); // "BAR"

const bazRef = fooBar(Baz);
bazRef.instance // (property) FooRef<Baz, number>.instance: Baz
console.log(bazRef.doIt().toFixed(2)) // "43.00"

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorial for more details.

A popular solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

class Foo<T> {
final Class<T> typeParameterClass;

public Foo(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}

public void bar() {
// you can access the typeParameterClass here and do whatever you like
}
}

Generic Return Type Based on Class

You can use the class-level Self for this:

extension NSObject {
class func create() -> Self {
return self.init()
}
}

let array = NSArray.create()

But I don't really see why you would, since you might as well just add an initializer.

C# Generic Return Type of Calling Object Type

One option is to use

public abstract class Serializable<T>
{
public T Copy()
{
return Deserialize(Serialize());
}

public string Serialize()
{
return JsonConvert.SerializeObject(this);
}

public static T Deserialize(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
}

And to implement it like this:

public class Test : Serializable<Test>
{
}

However, note that this will require you to continue the chain if you want further descendants of your class to have the most derived type:

public class Base<T> : Serializable<T> where T : Base<T>
{
}

public class Derived : Base<Derived>
{
}

This may or may not be acceptable depending on your use case.

TypeScript generic factory function to return type based on instance argument without overloading

You can change pick()'s call signature to be a generic method whose parameter is of a generic type constrained to the union of your different desired input types, and whose return type is a conditional type which chooses the output type based on the input type:

declare class Selection {
pick<T extends Car | Human>(what: T): T extends Car ? Garage : House;
}

This behaves the same for your example calls:

const selection = new Selection();
selection.pick(new Car()).lots; // compiler successfully autocompletes
selection.pick(new Human()).floors // compiler successfully autocompletes
selection.pick(new NotAllowed()); // compiler error

Unfortunately your implementation will not be seen as type safe; the type T will be unspecified inside of the body of pick(), and so the compiler does not and cannot verify that T is assignable to Car even if what instanceof Car is true:

pick<T extends Car | Human>(what: T): T extends Car ? Garage : House {
if (what instanceof Car) {
return new Garage(); // error!
// Type 'Garage' is not assignable to type 'T extends Car ? Garage : House'
} else if (what instanceof Human) {
return new House(); // error!
// Type 'House' is not assignable to type 'T extends Car ? Garage : House'.
} else {
throw Error('Not supported')
}
}

Instead of going on about why this happens, I will just direct you to microsoft/TypeScript#33912, the relevant open feature request to improve language support for implementing generic functions returning conditional types.

For now you will need to do something like a type assertion:

pick<T extends Car | Human>(what: T): T extends Car ? Garage : House {
if (what instanceof Car) {
return new Garage() as T extends Car ? Garage : House;
} else if (what instanceof Human) {
return new House() as T extends Car ? Garage : House;
} else {
throw Error('Not supported')
}
}

or equivalently, a single call-signature overload:

pick<T extends Car | Human>(what: T): T extends Car ? Garage : House;
pick(what: Car | Human) {
if (what instanceof Car) {
return new Garage();
} else if (what instanceof Human) {
return new House();
} else {
throw Error('Not supported')
}
}

I know you said you didn't want overloads, but this is usually my solution since adding type assertions everywhere is messier and more tedious.

Playground link to code

Typescript class method return type based on parameter

I actually found the answer I was looking for on how to use conditional typing on a class method with an interface.

It seems that you can use the interface with its conditional generic, without creating all the possible overloads, as long as you type cast the returned values from the implementation.

So according to my original post, this would be:

type ResType<T extends boolean> = T extends true ? string : boolean;

interface common3 {
test<T extends boolean>(flag: T): ResType<T>
}

class Exp3 implements common3 {
test<T extends boolean>(flag: T): ResType<T> {
if (flag) {
return 'truthy' as ResType<T>;
} else {
return false as ResType<T>;
}
}
}

const val3 = new Exp3().test(false); // boolean
const val4 = new Exp3().test(true); // string

Typescript Playground



Related Topics



Leave a reply



Submit