How do you infer the generic requirement (whats in the extends part) of a generic class?
// UserGivenClassThatCouldHaveAnyGeneric
class A<T extends string> {
constructor(a: T) {}
}
How would I infer that the generic T
must extends string
. I need this information in an conditional statement.
I have tried the following
type E<W> = any extends A<infer T> ? W extends T ? A<W> : void : void
E
should be void when given W
does not extend what is required by T
, but should be A when it does. However this does not work at all, event though there is no syntax error warning displayed.
My recommendation is to change E<W>
to something like this:
type E<W> = never extends A<infer T> ? [W] extends [T] ? A<W> : void : never
First, a conditional type where the checked type is any
, like any extends X? Y: Z
any extends X? Y: Z
, will end up being evaluated as the union Y | Z
Y | Z
for most types X
. That is, any
is special-cased to take both branches of the conditional type. See microsoft/TypeScript#27418 for some discussion about this. Assuming you were only trying to use the true branch of the conditional type, it would be better to use never extends X? Y: Z
never extends X? Y: Z
. Generally, never extends X
will always be true, and then the result will be Y
. Also, if there's ever a type you don't want to worry about cluttering up your unions, you should make it never
, not void
. The type A | never
A | never
evaluates to A
, whereas A | void
A | void
generally does not.
So let's change type E<W> = any extends A<infer T>? W extends T? A<W>: void: void
type E<W> = any extends A<infer T>? W extends T? A<W>: void: void
type E<W> = any extends A<infer T>? W extends T? A<W>: void: void
to type E<W> = never extends A<infer T>? W extends T? A<W>: void: never
type E<W> = never extends A<infer T>? W extends T? A<W>: void: never
type E<W> = never extends A<infer T>? W extends T? A<W>: void: never
.
Now the question that remains is: what do you want to do when W
is itself a union type? If you leave E<W>
defined with W extends T
instead of [W] extends [T]
, it will be treated as a distributive conditional type . That means if W
is a union type like A | B | C
A | B | C
A | B | C
, then E<W>
will evaluate the conditional for each member of that union and unite the results, producing the equivalent of E<A> | E<B> | E<C>
E<A> | E<B> | E<C>
E<A> | E<B> | E<C>
. Maybe that's what you want, but probably not. After all, if W
is string | number
string | number
, you probably want void
coming out, not A<string> | void
A<string> | void
, right?
The rule for when conditional types become distributive is that you are checking a "naked" or "bare" generic type parameter. Since W
is a generic type parameter, the conditional type W extends T? ...
W extends T? ...
will be distributive. The easiest way to turn off this behavior is to "clothe" the type parameter with something covariant, like a single-element tuple type. So it becomes [W] extends [T]? ...
[W] extends [T]? ...
.
Okay, hope that helps; good luck!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.