Reputation: 125
I declare enum and its transformer:
enum E { A, B };
type TRANSFORM<T extends E> = T extends E.A ? E.B : E.A;
And declare array type with two elements, where the second one is transformed type of the first element:
type qwe<T extends E> = [T, TRANSFORM<T>];
It's works fine, when i provide special enum element:
const element: qwe<E.A> = [E.A, E.A]; // Ok: Error: (Type 'E.A' is not assignable to type 'E.B')
How can i declare an array of this elements? The only way i found is:
const arr: (qwe<E.A> | qwe<E.B>)[] = [[E.A, E.A], [E.B, E.A]];
// Ok: Error: (Type '[E.A, E.A]' is not assignable to type '[E.A, E.B] | [E.B, E.A]')
But typing as
type b = qwe<E.A> | qwe<E.B>;
May be too large, when enum E
will contains more elements. How can i write this in short?
I was trying to declare like this:
type b = qwe<E>;
But it works not as expected. For example this array is correct:
const arr: qwe<E>[] = [[E.A, E.A], [E.B, E.A]];
Upvotes: 0
Views: 80
Reputation: 327624
Before I get started I'm going to change your type names to be more conventional (i.e., qwe
→ Qwe
and TRANSFORM
→ Transform
). I'm also going to change Transform
to be a lookup type instead of a conditional type:
type Transform<T extends E> = { [E.A]: E.B; [E.B]: E.A }[T];
This definition should evaluate to the same types as your intended use cases, (Transform<E.A>
is E.B
, Transform<E.B>
is E.A
, and Transform<E>
is E
), but the compiler will generally have an easier time making inferences about lookup types than it does about generic types. If you want you can change back to your original definition and the following should still work.
So, the solution I'd advise here is to make your Qwe<T>
type distribute over the union operation, so that Qwe<A | B>
will always evaluate to Qwe<A> | Qwe<B>
. TypeScript lets you do this via distributive conditional types, of the form T extends U ? X : Y
when T
is an unresolved generic type parameter. For example:
type Qwe<T extends E = E> = T extends any ? [T, Transform<T>] : never;
The T extends any ?
part looks like a no-op, but it has the important effect of preventing Qwe<E>
from evaluating to [E, E]
... instead, it will become [E.A, E.B] | [E.B, E.A]
as you desired.
Also I added a type parameter default so that when you write the type as just Qwe
it will be interpreted as Qwe<E>
... since I imagine that's the type you will want to use most often.
Okay, let's make sure this works as you desired:
const arr: Qwe[] = [[E.A, E.B], [E.B, E.A]]; // okay
const bad: Qwe[] = [[E.A, E.A]]; // error
Looks good to me. Hope that helps; good luck!
Upvotes: 2