From c9f0027926a4be72f0a14d1cabd5d9750e45cb01 Mon Sep 17 00:00:00 2001 From: Marcos Passos Date: Mon, 6 Feb 2023 10:51:53 -0300 Subject: [PATCH] Make component discriminator for slots explicit (#176) --- package-lock.json | 2 +- package.json | 2 +- src/slot.ts | 10 ++-------- test/slot.test.ts | 37 ++++++++++++++++++------------------- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index ce75f828..bc51a4ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "rollup": "^3.3.0", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", - "typescript": "^4.9.3" + "typescript": "^4.9.5" } }, "node_modules/@ampproject/remapping": { diff --git a/package.json b/package.json index ee64cafc..c4a66f9a 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/slot.ts b/src/slot.ts index ce17cbdd..4dd764ae 100644 --- a/src/slot.ts +++ b/src/slot.ts @@ -22,21 +22,15 @@ export interface VersionedSlotMap extends LatestSlotVersionMap { */ type Intersection = T extends infer O ? O & E : never; -type DiscriminatedSlotMap = { - [K in keyof VersionedSlotMap]: { - [V in keyof VersionedSlotMap[K]]: Intersection - } -}; - type UnionContent = { - [K in ComponentVersionId]: Intersection, {_component: K}>; + [K in ComponentVersionId]: Intersection, {_component: K | null}>; }; type UnknownContent = UnionContent[ComponentVersionId] extends never ? (JsonObject & {_component: string | null}) : UnionContent[ComponentVersionId]; -type VersionedContent = Versioned; +type VersionedContent = Versioned; export type DynamicSlotId = any; diff --git a/test/slot.test.ts b/test/slot.test.ts index 0826ffdd..24c7ef4f 100644 --- a/test/slot.test.ts +++ b/test/slot.test.ts @@ -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, }; } } @@ -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(' | '), ); }); @@ -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', () => { @@ -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', () => { @@ -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;})', ); }); @@ -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', () => { @@ -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', () => { @@ -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;})', ); });