简体   繁体   中英

typescript: Infer generic requirement of type

Question

How do you infer the generic requirement (whats in the extends part) of a generic class?

Explaination

// 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!

Playground link to code

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM