最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - TypeScript - get type of generic class parameter - Stack Overflow

programmeradmin4浏览0评论

I need to implement a type GetClassParameter<T> that would work like this:

class Foo<T> {

}

type foo = Foo<string>

type x = GetClassParameter<foo>; // should be string

I'm sorry if it's a duplicate, I couldn't find it. I only found a hardcoded solution (source):

type GetFooParameter<T extends Foo<any>> = T extends Foo<infer R> ? R : unknown;

I tried to do something like this:

class Foo<T> {
    public value: T;
    public value2: string;
    constructor (value: T) {
        this.value = value;
    }
}

type BaseT<T> = {
  value: T  // if removed, it wouldn't work
}
type GetClassParameter<T extends BaseT<any>> = T extends BaseT<infer R> ? R : unknown;

type x = GetClassParameter<foo> // string - almost works, requires shared property with a T

The above almost works but requires BaseT to have a value: T property.

Is there a way to do it without hardcoding anything, assuming the target class has only one generic parameter?

Update:

Another take, unsuccessful.

type ClassLike<T> = (new <T>(...args: any[]) => any);
type GetClassParameter<T extends ClassLike<any>> = T extends ClassLike<infer R> ? R : unknown;
type x = GetClassParameter<foo> // error, does not satisfy constraint

Update 2

It's not possible currently. Nevertheless, I tried a hack to define BaseT with value property and then removed it. It doesn't work. I'm adding it as a reference if someone had a similar idea to save you time. playground

Update 3

I'm adding a workaround I'm using to get the class parameter type for 2 classes that have nothing in mon (it can be extended to cover more classes just by adding additional conditional).

playground

class Alpha<T> {
    private a: T;
}

class Beta<T> {
    private b: T;
}

type GetClassParameterForAlphaBeta<T extends Alpha<any> | Beta<any>> =
    T extends Alpha<infer R>
    ? R : T extends Beta<infer R>
    ? R : unknown;

type alpha = Alpha<string>
type beta = Beta<number>

type x = GetClassParameterForAlphaBeta<alpha> // string
type y = GetClassParameterForAlphaBeta<beta> // number

I need to implement a type GetClassParameter<T> that would work like this:

class Foo<T> {

}

type foo = Foo<string>

type x = GetClassParameter<foo>; // should be string

I'm sorry if it's a duplicate, I couldn't find it. I only found a hardcoded solution (source):

type GetFooParameter<T extends Foo<any>> = T extends Foo<infer R> ? R : unknown;

I tried to do something like this:

class Foo<T> {
    public value: T;
    public value2: string;
    constructor (value: T) {
        this.value = value;
    }
}

type BaseT<T> = {
  value: T  // if removed, it wouldn't work
}
type GetClassParameter<T extends BaseT<any>> = T extends BaseT<infer R> ? R : unknown;

type x = GetClassParameter<foo> // string - almost works, requires shared property with a T

The above almost works but requires BaseT to have a value: T property.

Is there a way to do it without hardcoding anything, assuming the target class has only one generic parameter?

Update:

Another take, unsuccessful.

type ClassLike<T> = (new <T>(...args: any[]) => any);
type GetClassParameter<T extends ClassLike<any>> = T extends ClassLike<infer R> ? R : unknown;
type x = GetClassParameter<foo> // error, does not satisfy constraint

Update 2

It's not possible currently. Nevertheless, I tried a hack to define BaseT with value property and then removed it. It doesn't work. I'm adding it as a reference if someone had a similar idea to save you time. playground

Update 3

I'm adding a workaround I'm using to get the class parameter type for 2 classes that have nothing in mon (it can be extended to cover more classes just by adding additional conditional).

playground

class Alpha<T> {
    private a: T;
}

class Beta<T> {
    private b: T;
}

type GetClassParameterForAlphaBeta<T extends Alpha<any> | Beta<any>> =
    T extends Alpha<infer R>
    ? R : T extends Beta<infer R>
    ? R : unknown;

type alpha = Alpha<string>
type beta = Beta<number>

type x = GetClassParameterForAlphaBeta<alpha> // string
type y = GetClassParameterForAlphaBeta<beta> // number
Share Improve this question edited Apr 16, 2021 at 7:34 Maciej Kravchyk asked Apr 15, 2021 at 18:26 Maciej KravchykMaciej Kravchyk 16.7k7 gold badges70 silver badges81 bronze badges 8
  • Your updated try can take GetClassParameter<typeof Foo>, if that also works for you – hackape Commented Apr 15, 2021 at 18:50
  • You are right about that. However, passing GetClassParameter<typeof foo> doesn't seem to work. – Maciej Kravchyk Commented Apr 15, 2021 at 18:55
  • 1 And the feature that takes the general form of UtilType<F<T>> => T is not supported by TS. Higher kinded type is the term, and TS doesn’t have it. So forget perfect solution, best you can get is workarounds with limitations. Your two takes are actually good enough. – hackape Commented Apr 15, 2021 at 18:59
  • 1 I cannot find the issue anymore, but typescript omits the information of generics if they are unused. It is a design limitation. – Mirco S. Commented Apr 15, 2021 at 19:05
  • 1 I don’t have better answer for your problem, you’ve hit the limit of TS. FYI I wrote an answer about higher kinded type in TS years ago. Only remotely related to your problem. If you wanna dig deeper you can take a look. – hackape Commented Apr 15, 2021 at 19:08
 |  Show 3 more ments

1 Answer 1

Reset to default 4

Cannot be done yet - purely as a type. There is an open issue that aims to allow passing higher-kind generic types through: https://github./microsoft/TypeScript/issues/1213

Its an issue that is a bane to many trying to type highly moddable js-libs (like levelup).

Just to give you a much easier example of something that doesn't work:

interface Dummy<T> {};

declare function getParam<P, T extends Dummy<P>>(a: T): P;

let a = getParam(null as Dummy<string>);

here a is unknown

the only real work around is to move the generic parameter into the dummy interface as a fake property - but then everything passed to this will need that fake property defined too - or you're back to unknown

interface Dummy<T> {
  a?: T
};

declare function getParam<P, T extends Dummy<P>>(a: T): T["a"]

let a = getParam(null as Foo<string>);

a is now string but typescript still has no idea what P is

发布评论

评论列表(0)

  1. 暂无评论