1
class Base<T> {
    public state = {} as T;
    public getState(): T {
        return this.state
    }
    public setState(v: T) {
        this.state = v
    }
}

interface DogProps {
    name: 'hello';
    age: 123;
}

class Dog extends Base<DogProps> {
    public sayName() {
        console.log('name: ', this.state.name);
    }
    public sayAge() {
        console.log('age: ', this.state.age);
    }
}

function test<U, T extends Base<U>>(Cor: new () => T): [U, T] {
    const dog = new Cor();
    const state = dog.getState();
    return [state, dog];
}

const [state1, dog1] = test(Dog); // state1 is unknow

const [state2, dog2] = test<DogProps, Dog>(Dog); // verbose but right

demo playground

I am newbe in typescript.
I thought the code I wrote was right. But it does not work as expected.
Why state1's type is unknow?
Can I get the right type without test<DogProps, Dog>(Dog)?

much thanks!!!

threetree
  • 63
  • 9

1 Answers1

3

This is a side effect of how generic resolution works, typescript sees that T is referred to in the arguments so it tries to resolve it, but the constraint is based on U so it tries to resolve that first. Because U doesn't appear anywhere in the argument list, it can't resolve it so it ends up unknown

If you ensure that U is present in the arguments list you can ensure that typescript will be able to resolve it from just looking at the input without having to figure out T first:

function test<U, T extends Base<U>>(Cor: new()=>(T & Base<U>)): [U, T] {
                                                 // ^here^
}

This should fix the issue :)

Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
  • You are my god. You saved my life. – threetree Sep 16 '20 at 16:17
  • I sure hope your life doesn't depend on something like this . Happy coding! – Tadhg McDonald-Jensen Sep 16 '20 at 16:18
  • Now I understood the first paragraph, the reason why my code does not work. But I still not understand the second paragraph, why your code works. Especially `without having to figure out T first`. Because return param still need type T. Could you explain more details? – threetree Sep 16 '20 at 16:23
  • 2
    pretend you are typescript, you are looking at the types of arguments and you are trying to figure out what `U` should resolve to. But `U` isn't specified anywhere in the input arguments, only `T`, so how can you figure out what `U` is? At some point typescript will probably get updated to be able to cope with this case but at least for today using `(T & (Constraint of T))` works fine. – Tadhg McDonald-Jensen Sep 16 '20 at 16:26
  • 1
    As a human being, we know `Dog extends Base`, then `U's type` must be `DogProps`. But typescript seems not smart enough. By the way, could you recommend any books or website or articles to learn advanced typescript skills, like `how generic resolution works`. It seems that typescript document does not have these things. – threetree Sep 17 '20 at 01:47
  • Sorry I don't have any references, most of the stuff I've picked up has been from experimentation with the online [playground](https://www.typescriptlang.org/play). sorry I couldn't give you more general help. – Tadhg McDonald-Jensen Sep 17 '20 at 19:21