Add CRUD data store typing (#33680)
* Rename and export selectors for easier typing * Add CRUD action and selector type mapping * Add product attribute types * Separate getItem to provide correct return type * Rename config to type * Add generator return types
This commit is contained in:
parent
8d281241a9
commit
67d9ba3b19
|
@ -16,30 +16,29 @@ type SelectorOptions = {
|
||||||
pluralResourceName: string;
|
pluralResourceName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createSelectors = ( {
|
export const getItemCreateError = (
|
||||||
resourceName,
|
state: ResourceState,
|
||||||
pluralResourceName,
|
query: ItemQuery
|
||||||
}: SelectorOptions ) => {
|
) => {
|
||||||
const getCreateItemError = ( state: ResourceState, query: ItemQuery ) => {
|
|
||||||
const itemQuery = getResourceName( CRUD_ACTIONS.CREATE_ITEM, query );
|
const itemQuery = getResourceName( CRUD_ACTIONS.CREATE_ITEM, query );
|
||||||
return state.errors[ itemQuery ];
|
return state.errors[ itemQuery ];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeleteItemError = ( state: ResourceState, id: IdType ) => {
|
export const getItemDeleteError = ( state: ResourceState, id: IdType ) => {
|
||||||
const itemQuery = getResourceName( CRUD_ACTIONS.DELETE_ITEM, { id } );
|
const itemQuery = getResourceName( CRUD_ACTIONS.DELETE_ITEM, { id } );
|
||||||
return state.errors[ itemQuery ];
|
return state.errors[ itemQuery ];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItem = ( state: ResourceState, id: IdType ) => {
|
export const getItem = ( state: ResourceState, id: IdType ) => {
|
||||||
return state.data[ id ];
|
return state.data[ id ];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItemError = ( state: ResourceState, id: IdType ) => {
|
export const getItemError = ( state: ResourceState, id: IdType ) => {
|
||||||
const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEM, { id } );
|
const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEM, { id } );
|
||||||
return state.errors[ itemQuery ];
|
return state.errors[ itemQuery ];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItems = createSelector(
|
export const getItems = createSelector(
|
||||||
( state: ResourceState, query: ItemQuery ) => {
|
( state: ResourceState, query: ItemQuery ) => {
|
||||||
const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query );
|
const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query );
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ export const createSelectors = ( {
|
||||||
} );
|
} );
|
||||||
},
|
},
|
||||||
( state, query ) => {
|
( state, query ) => {
|
||||||
const itemQuery = getResourceName( resourceName, query );
|
const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query );
|
||||||
const ids = state.items[ itemQuery ]
|
const ids = state.items[ itemQuery ]
|
||||||
? state.items[ itemQuery ].data
|
? state.items[ itemQuery ].data
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -81,25 +80,29 @@ export const createSelectors = ( {
|
||||||
} ),
|
} ),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const getItemsError = ( state: ResourceState, query: ItemQuery ) => {
|
export const getItemsError = ( state: ResourceState, query: ItemQuery ) => {
|
||||||
const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query );
|
const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query );
|
||||||
return state.errors[ itemQuery ];
|
return state.errors[ itemQuery ];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUpdateItemError = ( state: ResourceState, id: IdType ) => {
|
export const getItemUpdateError = ( state: ResourceState, id: IdType ) => {
|
||||||
const itemQuery = getResourceName( CRUD_ACTIONS.UPDATE_ITEM, { id } );
|
const itemQuery = getResourceName( CRUD_ACTIONS.UPDATE_ITEM, { id } );
|
||||||
return state.errors[ itemQuery ];
|
return state.errors[ itemQuery ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createSelectors = ( {
|
||||||
|
resourceName,
|
||||||
|
pluralResourceName,
|
||||||
|
}: SelectorOptions ) => {
|
||||||
return {
|
return {
|
||||||
[ `get${ resourceName }` ]: getItem,
|
[ `get${ resourceName }` ]: getItem,
|
||||||
[ `get${ resourceName }Error` ]: getItemError,
|
[ `get${ resourceName }Error` ]: getItemError,
|
||||||
[ `get${ pluralResourceName }` ]: getItems,
|
[ `get${ pluralResourceName }` ]: getItems,
|
||||||
[ `get${ pluralResourceName }Error` ]: getItemsError,
|
[ `get${ pluralResourceName }Error` ]: getItemsError,
|
||||||
[ `getCreate${ resourceName }Error` ]: getCreateItemError,
|
[ `get${ resourceName }CreateError` ]: getItemCreateError,
|
||||||
[ `getDelete${ resourceName }Error` ]: getDeleteItemError,
|
[ `get${ resourceName }DeleteError` ]: getItemDeleteError,
|
||||||
[ `getUpdate${ resourceName }Error` ]: getUpdateItemError,
|
[ `get${ resourceName }UpdateError` ]: getItemUpdateError,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,8 +15,8 @@ describe( 'crud selectors', () => {
|
||||||
expect( selectors ).toHaveProperty( 'getProducts' );
|
expect( selectors ).toHaveProperty( 'getProducts' );
|
||||||
expect( selectors ).toHaveProperty( 'getProductError' );
|
expect( selectors ).toHaveProperty( 'getProductError' );
|
||||||
expect( selectors ).toHaveProperty( 'getProductsError' );
|
expect( selectors ).toHaveProperty( 'getProductsError' );
|
||||||
expect( selectors ).toHaveProperty( 'getCreateProductError' );
|
expect( selectors ).toHaveProperty( 'getProductCreateError' );
|
||||||
expect( selectors ).toHaveProperty( 'getDeleteProductError' );
|
expect( selectors ).toHaveProperty( 'getProductDeleteError' );
|
||||||
expect( selectors ).toHaveProperty( 'getUpdateProductError' );
|
expect( selectors ).toHaveProperty( 'getProductUpdateError' );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { BaseQueryParams } from '../types';
|
import { BaseQueryParams, WPDataSelector, WPDataSelectors } from '../types';
|
||||||
|
import {
|
||||||
|
getItem,
|
||||||
|
getItemError,
|
||||||
|
getItems,
|
||||||
|
getItemsError,
|
||||||
|
getItemCreateError,
|
||||||
|
getItemDeleteError,
|
||||||
|
getItemUpdateError,
|
||||||
|
} from './selectors';
|
||||||
|
|
||||||
export type IdType = number | string;
|
export type IdType = number | string;
|
||||||
|
|
||||||
|
@ -13,3 +22,86 @@ export type Item = {
|
||||||
export type ItemQuery = BaseQueryParams & {
|
export type ItemQuery = BaseQueryParams & {
|
||||||
[ key: string ]: unknown;
|
[ key: string ]: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CrudActions< ResourceName, ItemType, MutableProperties > =
|
||||||
|
MapActions<
|
||||||
|
{
|
||||||
|
create: ( query: ItemQuery ) => Item;
|
||||||
|
update: ( query: ItemQuery ) => Item;
|
||||||
|
},
|
||||||
|
ResourceName,
|
||||||
|
MutableProperties,
|
||||||
|
Generator< unknown, ItemType >
|
||||||
|
> &
|
||||||
|
MapActions<
|
||||||
|
{
|
||||||
|
delete: ( id: IdType ) => Item;
|
||||||
|
},
|
||||||
|
ResourceName,
|
||||||
|
IdType,
|
||||||
|
Generator< unknown, ItemType >
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type CrudSelectors<
|
||||||
|
ResourceName,
|
||||||
|
PluralResourceName,
|
||||||
|
ItemType,
|
||||||
|
ItemQueryType,
|
||||||
|
MutableProperties
|
||||||
|
> = MapSelectors<
|
||||||
|
{
|
||||||
|
'': WPDataSelector< typeof getItem >;
|
||||||
|
},
|
||||||
|
ResourceName,
|
||||||
|
IdType,
|
||||||
|
ItemType
|
||||||
|
> &
|
||||||
|
MapSelectors<
|
||||||
|
{
|
||||||
|
Error: WPDataSelector< typeof getItemError >;
|
||||||
|
DeleteError: WPDataSelector< typeof getItemDeleteError >;
|
||||||
|
UpdateError: WPDataSelector< typeof getItemUpdateError >;
|
||||||
|
},
|
||||||
|
ResourceName,
|
||||||
|
IdType,
|
||||||
|
unknown
|
||||||
|
> &
|
||||||
|
MapSelectors<
|
||||||
|
{
|
||||||
|
'': WPDataSelector< typeof getItems >;
|
||||||
|
},
|
||||||
|
PluralResourceName,
|
||||||
|
ItemQueryType,
|
||||||
|
ItemType[]
|
||||||
|
> &
|
||||||
|
MapSelectors<
|
||||||
|
{
|
||||||
|
Error: WPDataSelector< typeof getItemsError >;
|
||||||
|
},
|
||||||
|
PluralResourceName,
|
||||||
|
ItemQueryType,
|
||||||
|
unknown
|
||||||
|
> &
|
||||||
|
MapSelectors<
|
||||||
|
{
|
||||||
|
CreateError: WPDataSelector< typeof getItemCreateError >;
|
||||||
|
},
|
||||||
|
PluralResourceName,
|
||||||
|
MutableProperties,
|
||||||
|
unknown
|
||||||
|
> &
|
||||||
|
WPDataSelectors;
|
||||||
|
|
||||||
|
export type MapSelectors< Type, ResourceName, ParamType, ReturnType > = {
|
||||||
|
[ Property in keyof Type as `get${ Capitalize<
|
||||||
|
string & ResourceName
|
||||||
|
> }${ Capitalize< string & Property > }` ]: ( x: ParamType ) => ReturnType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MapActions< Type, ResourceName, ParamType, ReturnType > = {
|
||||||
|
[ Property in keyof Type as `${ Lowercase<
|
||||||
|
string & Property
|
||||||
|
> }${ Capitalize< string & ResourceName > }` ]: (
|
||||||
|
x: ParamType
|
||||||
|
) => ReturnType;
|
||||||
|
};
|
||||||
|
|
|
@ -119,6 +119,7 @@ import { OnboardingSelectors } from './onboarding/selectors';
|
||||||
import { OptionsSelectors } from './options/types';
|
import { OptionsSelectors } from './options/types';
|
||||||
import { ProductsSelectors } from './products/selectors';
|
import { ProductsSelectors } from './products/selectors';
|
||||||
import { OrdersSelectors } from './orders/selectors';
|
import { OrdersSelectors } from './orders/selectors';
|
||||||
|
import { ProductAttributeSelectors } from './product-attributes/types';
|
||||||
|
|
||||||
// As we add types to all the package selectors we can fill out these unknown types with real ones. See one
|
// As we add types to all the package selectors we can fill out these unknown types with real ones. See one
|
||||||
// of the already typed selectors for an example of how you can do this.
|
// of the already typed selectors for an example of how you can do this.
|
||||||
|
@ -148,6 +149,8 @@ export type WCSelectorType< T > = T extends typeof REVIEWS_STORE_NAME
|
||||||
? WPDataSelectors
|
? WPDataSelectors
|
||||||
: T extends typeof PRODUCTS_STORE_NAME
|
: T extends typeof PRODUCTS_STORE_NAME
|
||||||
? ProductsSelectors
|
? ProductsSelectors
|
||||||
|
: T extends typeof PRODUCT_ATTRIBUTES_STORE_NAME
|
||||||
|
? ProductAttributeSelectors
|
||||||
: T extends typeof ORDERS_STORE_NAME
|
: T extends typeof ORDERS_STORE_NAME
|
||||||
? OrdersSelectors
|
? OrdersSelectors
|
||||||
: never;
|
: never;
|
||||||
|
@ -158,4 +161,5 @@ export interface WCDataSelector {
|
||||||
|
|
||||||
// Other exports
|
// Other exports
|
||||||
export { ActionDispatchers as PluginsStoreActions } from './plugins/actions';
|
export { ActionDispatchers as PluginsStoreActions } from './plugins/actions';
|
||||||
|
export { ActionDispatchers as ProductAttributesActions } from './product-attributes/types';
|
||||||
export { ActionDispatchers as ProductsStoreActions } from './products/actions';
|
export { ActionDispatchers as ProductsStoreActions } from './products/actions';
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { DispatchFromMap } from '@automattic/data-stores';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { CrudActions, CrudSelectors } from '../crud/types';
|
||||||
|
|
||||||
|
type ProductAttribute = {
|
||||||
|
id: number;
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
order_by: string;
|
||||||
|
has_archives: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Query = {
|
||||||
|
context?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ReadOnlyProperties = 'id';
|
||||||
|
|
||||||
|
type MutableProperties = Partial<
|
||||||
|
Omit< ProductAttribute, ReadOnlyProperties >
|
||||||
|
>;
|
||||||
|
|
||||||
|
type ProductAttributeActions = CrudActions<
|
||||||
|
'ProductAttribute',
|
||||||
|
ProductAttribute,
|
||||||
|
MutableProperties
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ProductAttributeSelectors = CrudSelectors<
|
||||||
|
'ProductAttribute',
|
||||||
|
'ProductAttributes',
|
||||||
|
ProductAttribute,
|
||||||
|
Query,
|
||||||
|
MutableProperties
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ActionDispatchers = DispatchFromMap< ProductAttributeActions >;
|
Loading…
Reference in New Issue