From 5a125ce7b336553f1c29f73a5e35b9c500da7234 Mon Sep 17 00:00:00 2001 From: LydianLights Date: Thu, 29 Jun 2023 17:08:04 -0400 Subject: [PATCH 1/2] Add defaultProps and tests --- packages/solid/src/render/component.ts | 8 +++ packages/solid/test/component.spec.ts | 62 +++++++++++++++++---- packages/solid/test/component.type-tests.ts | 50 ++++++++++++++++- 3 files changed, 108 insertions(+), 12 deletions(-) diff --git a/packages/solid/src/render/component.ts b/packages/solid/src/render/component.ts index dc6ab4aa6..7a2269157 100644 --- a/packages/solid/src/render/component.ts +++ b/packages/solid/src/render/component.ts @@ -265,6 +265,14 @@ export function mergeProps(...sources: T): MergeProps { return target as any; } +export type DefaultProps = MergeProps<[Required>, T]>; +export function defaultProps( + defaults: Required>, + props: T +): DefaultProps { + return mergeProps(defaults, props); +} + export type SplitProps = [ ...{ [P in keyof K]: P extends `${number}` diff --git a/packages/solid/test/component.spec.ts b/packages/solid/test/component.spec.ts index b28156b7a..af1888e66 100644 --- a/packages/solid/test/component.spec.ts +++ b/packages/solid/test/component.spec.ts @@ -2,6 +2,7 @@ import { createRoot, createComponent, mergeProps, + defaultProps, splitProps, createUniqueId, createSignal, @@ -225,21 +226,60 @@ describe("mergeProps", () => { }); }); -describe("Set Default Props", () => { - test("simple set", () => { +describe("defaultProps", () => { + test("empty defaults", () => { let props: SimplePropTypes = { - get a() { - return "ji"; - }, + get a() { + return "beep"; + }, + b: null, + c: "boop" + }; + props = defaultProps({}, props); + expect(props.a).toBe("beep"); + expect(props.b).toBe(null); + expect(props.c).toBe("boop"); + expect(props.d).toBe(undefined); + }); + it("overwrites only undefined values", () => { + let props: SimplePropTypes = { + get a() { + return "beep"; + }, + b: null, + c: "boop" + }; + props = defaultProps( + { + a: "xxx", + b: "xxx", + c: "xxx", + d: "xxx" + }, + props + ); + expect(props.a).toBe("beep"); + expect(props.b).toBe(null); + expect(props.c).toBe("boop"); + expect(props.d).toBe("xxx"); + }); + it("allows null as a default", () => { + let props: SimplePropTypes = { + a: "defined", + c: null + }; + props = defaultProps( + { + a: null, b: null, - c: "j" + c: null }, - defaults: SimplePropTypes = { a: "yy", b: "ggg", d: "DD" }; - props = mergeProps(defaults, props); - expect(props.a).toBe("ji"); + props + ); + expect(props.a).toBe("defined"); expect(props.b).toBe(null); - expect(props.c).toBe("j"); - expect(props.d).toBe("DD"); + expect(props.c).toBe(null); + expect(props.d).toBe(undefined); }); }); diff --git a/packages/solid/test/component.type-tests.ts b/packages/solid/test/component.type-tests.ts index 94dee39e3..0d8424cd3 100644 --- a/packages/solid/test/component.type-tests.ts +++ b/packages/solid/test/component.type-tests.ts @@ -1,4 +1,4 @@ -import { mergeProps, splitProps } from "../src"; +import { mergeProps, defaultProps, splitProps } from "../src"; type Assert = never; // from: https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650 @@ -182,6 +182,54 @@ function M4( type TestM9 = Assert>; } +// d1: defaultProps preserves prop types +type D1Props = { + a?: string; + b: string; + c?: "one" | "two" | "three"; + d: "one" | "two" | "three"; + e?: [1, 2, 3]; + f: [1, 2, 3]; + g?: [string, number, "one" | "two", 3 | 4]; + h: [string, number, "one" | "two", 3 | 4]; + i?: null; + j: null; +}; +const d1 = defaultProps({}, {} as D1Props); +type D1 = typeof d1; +type TestD1 = Assert>; + +// d2: defaultProps removes undefined on merged props +const d2 = defaultProps( + { + a: "hello", + b: "two", + c: [1, 2, 3], + d: ["string", 99, "one", 4], + e: null + }, + {} as { + a?: string; + b?: "one" | "two" | "three"; + c?: [1, 2, 3]; + d?: [string, number, "one" | "two", 3 | 4]; + e?: null; + } +); +type D2 = typeof d2; +type TestD2 = Assert< + IsExact< + D2, + { + a: string; + b: "one" | "two" | "three"; + c: [1, 2, 3]; + d: [string, number, "one" | "two", 3 | 4]; + e: null; + } + > +>; + // s1-s3: splitProps return type is correct regardless of usage const s1 = splitProps({ a: 1, b: 2 }, ["a"]); type S1 = typeof s1; From 596a719967e4b6af1d34c93d063f02f95178a4c2 Mon Sep 17 00:00:00 2001 From: LydianLights Date: Sat, 1 Jul 2023 17:55:10 -0400 Subject: [PATCH 2/2] Switch defaultProps argument order --- packages/solid/src/render/component.ts | 4 +-- packages/solid/test/component.spec.ts | 30 +++++++++------------ packages/solid/test/component.type-tests.ts | 16 +++++------ 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/packages/solid/src/render/component.ts b/packages/solid/src/render/component.ts index 7a2269157..431de8b13 100644 --- a/packages/solid/src/render/component.ts +++ b/packages/solid/src/render/component.ts @@ -267,8 +267,8 @@ export function mergeProps(...sources: T): MergeProps { export type DefaultProps = MergeProps<[Required>, T]>; export function defaultProps( - defaults: Required>, - props: T + props: T, + defaults: Required> ): DefaultProps { return mergeProps(defaults, props); } diff --git a/packages/solid/test/component.spec.ts b/packages/solid/test/component.spec.ts index af1888e66..14ff12c1d 100644 --- a/packages/solid/test/component.spec.ts +++ b/packages/solid/test/component.spec.ts @@ -235,7 +235,7 @@ describe("defaultProps", () => { b: null, c: "boop" }; - props = defaultProps({}, props); + props = defaultProps(props, {}); expect(props.a).toBe("beep"); expect(props.b).toBe(null); expect(props.c).toBe("boop"); @@ -249,15 +249,12 @@ describe("defaultProps", () => { b: null, c: "boop" }; - props = defaultProps( - { - a: "xxx", - b: "xxx", - c: "xxx", - d: "xxx" - }, - props - ); + props = defaultProps(props, { + a: "xxx", + b: "xxx", + c: "xxx", + d: "xxx" + }); expect(props.a).toBe("beep"); expect(props.b).toBe(null); expect(props.c).toBe("boop"); @@ -268,14 +265,11 @@ describe("defaultProps", () => { a: "defined", c: null }; - props = defaultProps( - { - a: null, - b: null, - c: null - }, - props - ); + props = defaultProps(props, { + a: null, + b: null, + c: null + }); expect(props.a).toBe("defined"); expect(props.b).toBe(null); expect(props.c).toBe(null); diff --git a/packages/solid/test/component.type-tests.ts b/packages/solid/test/component.type-tests.ts index 0d8424cd3..1a1b6ced6 100644 --- a/packages/solid/test/component.type-tests.ts +++ b/packages/solid/test/component.type-tests.ts @@ -195,25 +195,25 @@ type D1Props = { i?: null; j: null; }; -const d1 = defaultProps({}, {} as D1Props); +const d1 = defaultProps({} as D1Props, {}); type D1 = typeof d1; type TestD1 = Assert>; // d2: defaultProps removes undefined on merged props const d2 = defaultProps( - { - a: "hello", - b: "two", - c: [1, 2, 3], - d: ["string", 99, "one", 4], - e: null - }, {} as { a?: string; b?: "one" | "two" | "three"; c?: [1, 2, 3]; d?: [string, number, "one" | "two", 3 | 4]; e?: null; + }, + { + a: "hello", + b: "two", + c: [1, 2, 3], + d: ["string", 99, "one", 4], + e: null } ); type D2 = typeof d2;