0

We've built a type generator for Typescript that outputs types as follows:

type Def$9f45cffb = {
    'title': string,
    'subtitle': string,
    'cta': Def$3f75f612,
};

type Def$3f75f612 = {
    'variant': 'primary' | 'secondary',
    'label': string,
    'link': string,
};

type Def$46b580bc = {
    'title': string,
    'subtitle'?: string,
    'cta': Def$3f75f612,
};

declare module 'component' {
    type VerticalBannerV1_0 = Def$9f45cffb;

    export interface VersionedComponentMap {
        'vertical-banner': {
            latest: VerticalBannerV1_0,
            '1.0': VerticalBannerV1_0,
        };
    }
}

declare module 'slot' {
    type HomeBannerV1_0 = Def$46b580bc;
    type HomeBannerV2_0 = Def$9f45cffb;

    export interface VersionedSlotMap {
        'home-banner': {
            latest: HomeBannerV2_0,
            '1.0': HomeBannerV1_0,
            '2.0': HomeBannerV2_0,
        };
    }
}

Notice that types are reused between the two mappings to avoid duplication. This works pretty well, except that the aliases are expanded, making debugging a nightmare:

Type expansion

I know that TypeScript doesn't offer guarantees about preserving type aliases. I'm looking for a workaround for this issue to provide a better developer experience.

My goal is to prevent aliases from getting expanded so HomeBannerV2_0 would not become Def$46b580bc, for example.

This is what I get currently for type example = VersionedSlotMap['home-banner'][keyof VersionedSlotMap['home-banner']]; }:

Def$46b580bc | Def$9f45cffb

And this is what I want:

HomeBannerV1_0 | HomeBannerV2_0
Marcos Passos
  • 400
  • 1
  • 3
  • 15
  • 1
    It's not entirely clear what you are asking... but does the [`ExpandRecursively` type described here](https://stackoverflow.com/a/57683652/14357) help? https://tsplay.dev/wXOQ9W – spender Feb 13 '23 at 12:53
  • It's exactly the opposite. I've edited the question to make it more explicit. – Marcos Passos Feb 13 '23 at 14:09

1 Answers1

1

I found that tagging the type using a recursive utility and then removing that tag works:

const Complicated = Symbol();

type Complicated<T> = Omit<T & { [Complicated]: Complicated<T> }, typeof Complicated>;

Usage:

// Hovering over types with `IWantToSeeThis` only displays `IWantToSeeThis`
type IWantToSeeThis = Complicated<IDontWantToSeeThis>;

However, this only works with primitives and objects. Arrays and tuples will not work and functions will just get reduced to {}.

You could get around this by keeping the tag:

type Complicated<T> = T & { [Complicated]: Complicated<T> };

but then you would have problems when trying to assign values to this type.

Playground for the simple example above

Playground for your code

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
kelsny
  • 23,009
  • 3
  • 19
  • 48