From 227b9dc95fdcee95cef159d4f96992fa33dc609d Mon Sep 17 00:00:00 2001 From: brunnerh Date: Sat, 4 May 2024 20:24:39 +0200 Subject: [PATCH] Tweaks to DataTable generics (#1968) Adjust DataTable types. - Make key in `cell` slot prop less strict to prevent type errors in markup. - Resolve property path names up to one level deep for header keys. --- COMPONENT_INDEX.md | 5 +++-- docs/src/COMPONENT_API.json | 16 +++++++------- src/DataTable/DataTable.svelte | 28 ++++++++++++++++++++---- src/DataTable/DataTableTypes.d.ts | 17 ++++++++++++++ tests/DataTableAppendColumns.test.svelte | 6 +---- types/DataTable/DataTable.svelte.d.ts | 5 +++-- types/DataTable/DataTableTypes.d.ts | 17 ++++++++++++++ 7 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 src/DataTable/DataTableTypes.d.ts create mode 100644 types/DataTable/DataTableTypes.d.ts diff --git a/COMPONENT_INDEX.md b/COMPONENT_INDEX.md index bff81593ec..ca221a7058 100644 --- a/COMPONENT_INDEX.md +++ b/COMPONENT_INDEX.md @@ -926,7 +926,8 @@ None. ### Types ```ts -export type DataTableKey = Exclude; +export type DataTableKey = + import("./DataTableTypes.d.ts").PropertyPath; export type DataTableValue = any; @@ -962,7 +963,7 @@ export interface DataTableRow { export type DataTableRowId = any; export interface DataTableCell { - key: DataTableKey; + key: DataTableKey | (string & {}); value: DataTableValue; display?: (item: Value, row: DataTableRow) => DataTableValue; } diff --git a/docs/src/COMPONENT_API.json b/docs/src/COMPONENT_API.json index 560fd509cc..07834bab90 100644 --- a/docs/src/COMPONENT_API.json +++ b/docs/src/COMPONENT_API.json @@ -2712,9 +2712,9 @@ ], "typedefs": [ { - "type": "Exclude", + "type": "import('./DataTableTypes.d.ts').PropertyPath", "name": "DataTableKey", - "ts": "type DataTableKey = Exclude" + "ts": "type DataTableKey = import('./DataTableTypes.d.ts').PropertyPath" }, { "type": "any", @@ -2722,14 +2722,14 @@ "ts": "type DataTableValue = any" }, { - "type": "{ key: DataTableKey; empty: boolean; display?: (item: Value, row: Row) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => number); columnMenu?: boolean; width?: string; minWidth?: string; }", + "type": "{\n key: DataTableKey;\n empty: boolean;\n display?: (item: Value, row: Row) => DataTableValue;\n sort?: false | ((a: DataTableValue, b: DataTableValue) => number);\n columnMenu?: boolean;\n width?: string;\n minWidth?: string;\n}", "name": "DataTableEmptyHeader", - "ts": "interface DataTableEmptyHeader { key: DataTableKey; empty: boolean; display?: (item: Value, row: Row) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => number); columnMenu?: boolean; width?: string; minWidth?: string; }" + "ts": "interface DataTableEmptyHeader {\n key: DataTableKey;\n empty: boolean;\n display?: (item: Value, row: Row) => DataTableValue;\n sort?: false | ((a: DataTableValue, b: DataTableValue) => number);\n columnMenu?: boolean;\n width?: string;\n minWidth?: string;\n}" }, { - "type": "{ key: DataTableKey; value: DataTableValue; display?: (item: Value, row: Row) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => number); columnMenu?: boolean; width?: string; minWidth?: string; }", + "type": "{\n key: DataTableKey;\n value: DataTableValue;\n display?: (item: Value, row: Row) => DataTableValue;\n sort?: false | ((a: DataTableValue, b: DataTableValue) => number);\n columnMenu?: boolean;\n width?: string;\n minWidth?: string;\n}", "name": "DataTableNonEmptyHeader", - "ts": "interface DataTableNonEmptyHeader { key: DataTableKey; value: DataTableValue; display?: (item: Value, row: Row) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => number); columnMenu?: boolean; width?: string; minWidth?: string; }" + "ts": "interface DataTableNonEmptyHeader {\n key: DataTableKey;\n value: DataTableValue;\n display?: (item: Value, row: Row) => DataTableValue;\n sort?: false | ((a: DataTableValue, b: DataTableValue) => number);\n columnMenu?: boolean;\n width?: string;\n minWidth?: string;\n}" }, { "type": "DataTableNonEmptyHeader | DataTableEmptyHeader", @@ -2747,9 +2747,9 @@ "ts": "type DataTableRowId = any" }, { - "type": "{ key: DataTableKey; value: DataTableValue; display?: (item: Value, row: DataTableRow) => DataTableValue; }", + "type": "{\n key: DataTableKey | (string & {});\n value: DataTableValue;\n display?: (item: Value, row: DataTableRow) => DataTableValue;\n}", "name": "DataTableCell", - "ts": "interface DataTableCell { key: DataTableKey; value: DataTableValue; display?: (item: Value, row: DataTableRow) => DataTableValue; }" + "ts": "interface DataTableCell {\n key: DataTableKey | (string & {});\n value: DataTableValue;\n display?: (item: Value, row: DataTableRow) => DataTableValue;\n}" } ], "generics": ["Row", "Row extends DataTableRow = DataTableRow"], diff --git a/src/DataTable/DataTable.svelte b/src/DataTable/DataTable.svelte index 4e7f6a0900..b02398186b 100644 --- a/src/DataTable/DataTable.svelte +++ b/src/DataTable/DataTable.svelte @@ -2,14 +2,34 @@ /** * @generics {Row extends DataTableRow = DataTableRow} Row * @template {DataTableRow} Row - * @typedef {Exclude} DataTableKey + * @typedef {import('./DataTableTypes.d.ts').PropertyPath} DataTableKey * @typedef {any} DataTableValue - * @typedef {{ key: DataTableKey; empty: boolean; display?: (item: Value, row: Row) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => number); columnMenu?: boolean; width?: string; minWidth?: string; }} DataTableEmptyHeader - * @typedef {{ key: DataTableKey; value: DataTableValue; display?: (item: Value, row: Row) => DataTableValue; sort?: false | ((a: DataTableValue, b: DataTableValue) => number); columnMenu?: boolean; width?: string; minWidth?: string; }} DataTableNonEmptyHeader + * @typedef {{ + * key: DataTableKey; + * empty: boolean; + * display?: (item: Value, row: Row) => DataTableValue; + * sort?: false | ((a: DataTableValue, b: DataTableValue) => number); + * columnMenu?: boolean; + * width?: string; + * minWidth?: string; + * }} DataTableEmptyHeader + * @typedef {{ + * key: DataTableKey; + * value: DataTableValue; + * display?: (item: Value, row: Row) => DataTableValue; + * sort?: false | ((a: DataTableValue, b: DataTableValue) => number); + * columnMenu?: boolean; + * width?: string; + * minWidth?: string; + * }} DataTableNonEmptyHeader * @typedef {DataTableNonEmptyHeader | DataTableEmptyHeader} DataTableHeader * @typedef {{ id: any; [key: string]: DataTableValue; }} DataTableRow * @typedef {any} DataTableRowId - * @typedef {{ key: DataTableKey; value: DataTableValue; display?: (item: Value, row: DataTableRow) => DataTableValue; }} DataTableCell + * @typedef {{ + * key: DataTableKey | (string & {}); + * value: DataTableValue; + * display?: (item: Value, row: DataTableRow) => DataTableValue; + * }} DataTableCell * @slot {{ row: Row; }} expanded-row * @slot {{ header: DataTableNonEmptyHeader; }} cell-header * @slot {{ row: Row; cell: DataTableCell; rowIndex: number; cellIndex: number; }} cell diff --git a/src/DataTable/DataTableTypes.d.ts b/src/DataTable/DataTableTypes.d.ts new file mode 100644 index 0000000000..176140fd17 --- /dev/null +++ b/src/DataTable/DataTableTypes.d.ts @@ -0,0 +1,17 @@ +type PathDepth = [never, 0, 1, 2, ...0[]]; + +type Join = K extends string | number + ? P extends string | number + ? `${K}${"" extends P ? "" : "."}${P}` + : never + : never; + +export type PropertyPath = [D] extends [never] + ? never + : T extends object + ? { + [K in keyof T]-?: K extends string | number + ? `${K}` | Join> + : never; + }[keyof T] + : ""; diff --git a/tests/DataTableAppendColumns.test.svelte b/tests/DataTableAppendColumns.test.svelte index c7ca71808f..4d952731f7 100644 --- a/tests/DataTableAppendColumns.test.svelte +++ b/tests/DataTableAppendColumns.test.svelte @@ -54,11 +54,7 @@ ]; - + {#if cell.key === "overflow"} diff --git a/types/DataTable/DataTable.svelte.d.ts b/types/DataTable/DataTable.svelte.d.ts index 1013ac1f4f..8df8eca778 100644 --- a/types/DataTable/DataTable.svelte.d.ts +++ b/types/DataTable/DataTable.svelte.d.ts @@ -1,7 +1,8 @@ import type { SvelteComponentTyped } from "svelte"; import type { SvelteHTMLElements } from "svelte/elements"; -export type DataTableKey = Exclude; +export type DataTableKey = + import("./DataTableTypes.d.ts").PropertyPath; export type DataTableValue = any; @@ -37,7 +38,7 @@ export interface DataTableRow { export type DataTableRowId = any; export interface DataTableCell { - key: DataTableKey; + key: DataTableKey | (string & {}); value: DataTableValue; display?: (item: Value, row: DataTableRow) => DataTableValue; } diff --git a/types/DataTable/DataTableTypes.d.ts b/types/DataTable/DataTableTypes.d.ts new file mode 100644 index 0000000000..176140fd17 --- /dev/null +++ b/types/DataTable/DataTableTypes.d.ts @@ -0,0 +1,17 @@ +type PathDepth = [never, 0, 1, 2, ...0[]]; + +type Join = K extends string | number + ? P extends string | number + ? `${K}${"" extends P ? "" : "."}${P}` + : never + : never; + +export type PropertyPath = [D] extends [never] + ? never + : T extends object + ? { + [K in keyof T]-?: K extends string | number + ? `${K}` | Join> + : never; + }[keyof T] + : "";