Skip to content

Commit

Permalink
Make component discriminator for slots explicit (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcospassos authored Feb 6, 2023
1 parent caa7ff7 commit c9f0027
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 29 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"rollup": "^3.3.0",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"
"typescript": "^4.9.5"
},
"browserslist": [
"last 1 version"
Expand Down
10 changes: 2 additions & 8 deletions src/slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,15 @@ export interface VersionedSlotMap extends LatestSlotVersionMap {
*/
type Intersection<T, E> = T extends infer O ? O & E : never;

type DiscriminatedSlotMap = {
[K in keyof VersionedSlotMap]: {
[V in keyof VersionedSlotMap[K]]: Intersection<VersionedSlotMap[K][V], {_component: string | null}>
}
};

type UnionContent = {
[K in ComponentVersionId]: Intersection<ComponentContent<K>, {_component: K}>;
[K in ComponentVersionId]: Intersection<ComponentContent<K>, {_component: K | null}>;
};

type UnknownContent = UnionContent[ComponentVersionId] extends never
? (JsonObject & {_component: string | null})
: UnionContent[ComponentVersionId];

type VersionedContent<I extends VersionedSlotId> = Versioned<I, DiscriminatedSlotMap, UnknownContent>;
type VersionedContent<I extends VersionedSlotId> = Versioned<I, VersionedSlotMap, UnknownContent>;

export type DynamicSlotId = any;

Expand Down
37 changes: 18 additions & 19 deletions test/slot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ describe('Slot typing', () => {
};
declare module '../src/slot' {
type HomeBannerV1 = HomeBanner & {_component: 'banner@1' | null};
type HybridBannerV1 = HybridBanner & {_component: 'hybrid-banner@1' | null};
interface VersionedSlotMap {
'home-banner': {
'latest': HomeBanner,
'1': HomeBanner,
'latest': HomeBannerV1,
'1': HomeBannerV1,
};
'hybrid-banner': {
'latest': HybridBanner,
'1': HybridBanner,
'latest': HybridBannerV1,
'1': HybridBannerV1,
};
}
}
Expand Down Expand Up @@ -165,12 +168,12 @@ describe('Slot typing', () => {

expect(getTypeName(code)).toBe(
[
'"hybrid-banner@1"',
'"home-banner"',
'"hybrid-banner"',
'"home-banner@latest"',
'"home-banner@1"',
'"hybrid-banner@latest"',
'"hybrid-banner@1"',
].join(' | '),
);
});
Expand Down Expand Up @@ -222,7 +225,7 @@ describe('Slot typing', () => {

expect(() => compileCode(code)).not.toThrow();

expect(getTypeName(code)).toBe('"home-banner@1" | "hybrid-banner@1"');
expect(getTypeName(code)).toBe('"hybrid-banner@1" | "home-banner@1"');
});

it('should export a CompatibleSlotContent type that resolves to never when no slot mapping exists', () => {
Expand All @@ -249,7 +252,7 @@ describe('Slot typing', () => {

expect(() => compileCode(code)).not.toThrow();

expect(getTypeName(code)).toBe('Banner & {_component: "banner@1";}');
expect(getTypeName(code)).toBe('Banner & {_component: "banner@1" | null;}');
});

it('should export a CompatibleSlotContent type that resolves the slot content type for multiple components', () => {
Expand All @@ -265,9 +268,9 @@ describe('Slot typing', () => {
expect(() => compileCode(code)).not.toThrow();

expect(getTypeName(code)).toBe(
'(Banner & {_component: "banner@1";})'
+ ' | (HorizontalBanner & {_type: \'horizontal-banner\';} & {_component: "hybrid-banner@1";})'
+ ' | (VerticalBanner & {_type: \'vertical-banner\';} & {_component: "hybrid-banner@1";})',
'(Banner & {_component: "banner@1" | null;})'
+ ' | (HorizontalBanner & {_type: \'horizontal-banner\';} & {_component: "hybrid-banner@1" | null;})'
+ ' | (VerticalBanner & {...;} & {_component: "hybrid-banner@1" | null;})',
);
});

Expand Down Expand Up @@ -299,7 +302,7 @@ describe('Slot typing', () => {

expect(() => compileCode(code)).not.toThrow();

expect(getTypeName(code)).toBe('HomeBanner & {_component: string | null;}');
expect(getTypeName(code)).toBe('HomeBanner & {_component: \'banner@1\' | null;}');
});

it('should export a SlotContent type that resolves to multiple slot contents', () => {
Expand All @@ -314,11 +317,7 @@ describe('Slot typing', () => {

expect(() => compileCode(code)).not.toThrow();

expect(getTypeName(code)).toBe(
'(HomeBanner & {_component: string | null;}) | '
+ "(HorizontalBanner & {_type: 'horizontal-banner';} & {_component: string | null;}) "
+ '| (VerticalBanner & {...;} & {_component: string | null;})',
);
expect(getTypeName(code)).toBe('HomeBannerV1 | HybridBannerV1');
});

it('should export a SlotContent type that resolves to JsonObject when the slot is unknown', () => {
Expand Down Expand Up @@ -359,9 +358,9 @@ describe('Slot typing', () => {
expect(() => compileCode(code)).not.toThrow();

expect(getTypeName(code)).toBe(
'(Banner & {_component: "banner@1";})'
+ ' | (HorizontalBanner & {_type: \'horizontal-banner\';} & {_component: "hybrid-banner@1";})'
+ ' | (VerticalBanner & {_type: \'vertical-banner\';} & {_component: "hybrid-banner@1";})',
'(Banner & {_component: "banner@1" | null;})'
+ ' | (HorizontalBanner & {_type: \'horizontal-banner\';} & {_component: "hybrid-banner@1" | null;})'
+ ' | (VerticalBanner & {...;} & {_component: "hybrid-banner@1" | null;})',
);
});

Expand Down

0 comments on commit c9f0027

Please sign in to comment.