Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Better types
  • Loading branch information
oandregal committed Nov 13, 2025
commit 988a195e880a87f8081ad5fbdcbd382e08f8fb8b
17 changes: 15 additions & 2 deletions packages/dataviews/src/types/field-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ export type DayString =
| 'friday'
| 'saturday';

export type NormalizedField< Item > = Omit< Field< Item >, 'Edit' > & {
type NormalizedFieldBase< Item > = Omit< Field< Item >, 'Edit' > & {
label: string;
header: string | ReactElement;
getValue: ( args: { item: Item } ) => any;
Expand All @@ -351,9 +351,22 @@ export type NormalizedField< Item > = Omit< Field< Item >, 'Edit' > & {
enableSorting: boolean;
filterBy: NormalizedFilterByConfig | false;
readOnly: boolean;
format: {} | Required< FormatDate >;
};

type NormalizedFieldDate< Item > = NormalizedFieldBase< Item > & {
type: 'date';
format: Required< FormatDate >;
};

type NormalizedFieldGeneric< Item > = NormalizedFieldBase< Item > & {
type?: Exclude< FieldType, 'date' >;
format: {};
};

export type NormalizedField< Item > =
| NormalizedFieldGeneric< Item >
| NormalizedFieldDate< Item >;

/**
* A collection of dataview fields for a data type.
*/
Expand Down
59 changes: 40 additions & 19 deletions packages/dataviews/src/utils/normalize-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,24 +189,22 @@ export default function normalizeFields< Item >(

const filterBy = getFilterBy( field, fieldTypeDefinition );

let format = {};
if ( field.type === 'date' ) {
format = {
date:
field.format?.date !== undefined &&
typeof field.format.date === 'string'
? field.format.date
: getSettings().formats.date,
weekStartsOn: DAYS_OF_WEEK.includes(
field.format?.weekStartsOn as DayString
)
? field?.format?.weekStartsOn
: numberToWeekStartsOn( getSettings().l10n.startOfWeek ),
};
}

return {
...field,
/**
* NormalizedField is a discriminated union type: the shape of the format property
* depends on the type property. For example, for the 'date' type, the format
* contains date or weekStartsOn — which are not valid for other types.
*
* Being type and format interdependent, we need to write the code
* in a way that TypeScript is able to statically infer the types.
* That's why we have a return branch for every item in the union type.
*
* See a longer explanation with examples at
* https://github.com/WordPress/gutenberg/pull/72999#discussion_r2523145453
*/
const { type, ...fieldWithoutType } = field;

const baseField = {
...fieldWithoutType,
label: field.label || field.id,
header: field.header || field.label || field.id,
getValue,
Expand All @@ -223,7 +221,30 @@ export default function normalizeFields< Item >(
true,
filterBy,
readOnly: field.readOnly ?? fieldTypeDefinition.readOnly ?? false,
format,
format: {},
};

if ( field.type === 'date' ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move this to a "normalizeField" function within each type? Or just avoid the need for the normalization in the first place.

These "utils" functions shouldn't have any field.type === something
I'm a bit worried that all these edge cases are piling up instead of actually being used to improve the field types definitions API

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good call, I've started to look into this at #73387

const format = {
date:
field.format?.date !== undefined &&
typeof field.format.date === 'string'
? field.format.date
: getSettings().formats.date,
weekStartsOn: DAYS_OF_WEEK.includes(
field.format?.weekStartsOn as DayString
)
? field?.format?.weekStartsOn
: numberToWeekStartsOn( getSettings().l10n.startOfWeek ),
};

return {
...baseField,
type: 'date',
format,
};
}

return { ...baseField, type: field.type, format: {} };
} );
}