Maroš Beťko
Maroš Beťko

Reputation: 2329

Typescript conditional mapped type with multiple conditions

I am working with a normalized data inside redux store (all complex types are just ids referring to real object) and I would like to create a denormalized types using typescript's conditional mapped types feature.

I keep the actual type of the object the id is referring to as it's generic argument and from this I was able to create denormalized type correctly if only non array id attributes are used.

type Id<T extends HandlerBase<any>> = string & { __type: T };

class HandlerBase<T extends HandlerBase<T>> {};

class HandlerA extends HandlerBase<HandlerA> {
    str: string;
    b: Id<HandlerB>;
    bArr: Id<HandlerB>[];
};

class HandlerB extends HandlerBase<HandlerA> {};

type DenormalizedHandler<T> = {
    [P in keyof T]: T[P] extends Id<infer U> ? DenormalizedHandler<U> : T[P];
}

const handler: DenormalizedHandler<HandlerA> = undefined;
handler.str; // Is string
handler.b; // Is DenormalizedHandler<HandlerB>
handler.bArr; // Should be DenormalizedHandler<HandlerB>[]

Now I need to figure out how to possibly add second condition to DenormalizedHandler so it can correctly map Id<U> to DenormalizedHandler<U> and Id<U>[] to DenormalizedHandler<U>[].

Upvotes: 2

Views: 9315

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249466

You just need to add another condition, conditional types can nest just like ternary expressions could:

type Id<T extends HandlerBase<any>> = string & { __type: T };

class HandlerBase<T extends HandlerBase<T>> {};

class HandlerA extends HandlerBase<HandlerA> {
    str: string;
    b: Id<HandlerB>;
    bArr: Id<HandlerB>[];
};

class HandlerB extends HandlerBase<HandlerA> {};

type DenormalizedHandler<T> = {
    [P in keyof T]: 
        T[P] extends Id<infer U> ? DenormalizedHandler<U> : 
        T[P] extends Array<Id<infer U>> ? Array<DenormalizedHandler<U>> : 
        T[P];
}

const handler: DenormalizedHandler<HandlerA> = undefined;
handler.str; // Is string
handler.b; // Is DenormalizedHandler<HandlerB>
handler.bArr; // Is DenormalizedHandler<HandlerB>[]

Upvotes: 5

Related Questions