diff --git a/.eslintrc.cjs b/.eslintrc.cjs index f1fd21e..d6fb16a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -4,4 +4,7 @@ module.exports = { env: { es6: true, }, + rules: { + '@typescript-eslint/ban-types': 0, + }, } diff --git a/package.json b/package.json index c31956d..d64cc44 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "flow-bin": "^0.97.0", "mindfront-redux-utils": "^2.0.0", "mocha": "^10.2.0", - "redux": "^3.6.0", + "redux": "^4.2.1", "rimraf": "^2.6.0", "sinon": "^1.17.6", "typescript": "^5.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 384f39d..bbc076b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,8 +74,8 @@ devDependencies: specifier: ^10.2.0 version: 10.2.0 redux: - specifier: ^3.6.0 - version: 3.7.2 + specifier: ^4.2.1 + version: 4.2.1 rimraf: specifier: ^2.6.0 version: 2.7.1 @@ -4977,9 +4977,11 @@ packages: /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + requiresBuild: true dependencies: js-tokens: 4.0.0 dev: true + optional: true /loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} @@ -5941,12 +5943,20 @@ packages: /redux@3.7.2: resolution: {integrity: sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==} + requiresBuild: true dependencies: lodash: 4.17.21 lodash-es: 4.17.21 loose-envify: 1.4.0 symbol-observable: 1.2.0 dev: true + optional: true + + /redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + dependencies: + '@babel/runtime': 7.23.2 + dev: true /regenerate-unicode-properties@10.1.1: resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} @@ -6608,7 +6618,9 @@ packages: /symbol-observable@1.2.0: resolution: {integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true + optional: true /temp-dir@3.0.0: resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} diff --git a/src/actions.d.ts b/src/actions.d.ts index d338df0..880f6d8 100644 --- a/src/actions.d.ts +++ b/src/actions.d.ts @@ -1,3 +1,4 @@ +import { AnyAction } from 'redux' import type { Feature, FeatureState, FeatureAction } from './index' export declare const ACTION_TYPE_PREFIX = '@@redux-features/' export declare const ADD_FEATURE: '@@redux-features/ADD_FEATURE' @@ -6,19 +7,19 @@ export declare const LOAD_FEATURE: '@@redux-features/LOAD_FEATURE' export declare const INSTALL_FEATURE: '@@redux-features/INSTALL_FEATURE' export declare const SET_FEATURE_STATE: '@@redux-features/SET_FEATURE_STATE' export declare const LOAD_INITIAL_FEATURES: '@@redux-features/LOAD_INITIAL_FEATURES' -export declare function addFeature( +export declare function addFeature( id: string, - feature: Feature -): FeatureAction -export declare function replaceFeature( - id: string, - feature: Feature + feature: Feature ): FeatureAction +export declare function replaceFeature< + S = any, + A extends AnyAction = AnyAction +>(id: string, feature: Feature): FeatureAction export declare function loadFeature(id: string): FeatureAction -export declare function installFeature( - id: string, - feature: Feature -): FeatureAction +export declare function installFeature< + S = any, + A extends AnyAction = AnyAction +>(id: string, feature: Feature): FeatureAction export declare function setFeatureState( id: string, payload: FeatureState diff --git a/src/defaults.d.ts b/src/defaults.d.ts index dbc0ec0..da8db3f 100644 --- a/src/defaults.d.ts +++ b/src/defaults.d.ts @@ -1,17 +1,28 @@ -import type { Reducer, Middleware } from 'redux' -export declare const defaultCreateReducer: (( - initialState: S, - reducers: { - [actionType: string]: Reducer - } -) => Reducer) & - ((reducers: { [actionType: string]: Reducer }) => Reducer) -export declare function defaultComposeReducers( - ...reducers: Array> -): Reducer -export declare function defaultCreateMiddleware(middlewares: { - [actionType: string]: Middleware -}): Middleware -export declare function defaultComposeMiddleware( - ...middlewares: Array -): Middleware +import type { Reducer, Middleware, AnyAction, Dispatch } from 'redux' +export declare const defaultCreateReducer: { + ( + initialState: S, + reducers: Record> + ): Reducer + ( + reducers: Record> + ): Reducer +} +export declare function defaultComposeReducers< + S = any, + A extends AnyAction = AnyAction +>(...reducers: Array>): Reducer +export declare function defaultCreateMiddleware< + DispatchExt = {}, + S = any, + D extends Dispatch = Dispatch +>( + middlewares: Record> +): Middleware +export declare function defaultComposeMiddleware< + DispatchExt = {}, + S = any, + D extends Dispatch = Dispatch +>( + ...middlewares: Array> +): Middleware diff --git a/src/featureMiddlewaresMiddleware.d.ts b/src/featureMiddlewaresMiddleware.d.ts index 83d7c72..816596a 100644 --- a/src/featureMiddlewaresMiddleware.d.ts +++ b/src/featureMiddlewaresMiddleware.d.ts @@ -1,6 +1,10 @@ -import type { Middleware } from 'redux' +import type { Dispatch, Middleware } from 'redux' import type { Features, ComposeMiddleware } from './index' -export default function featureMiddlewaresMiddleware(config?: { - getFeatures?: (state: S) => Features | null | undefined - composeMiddleware?: ComposeMiddleware -}): Middleware +export default function featureMiddlewaresMiddleware< + DispatchExt = {}, + S = any, + D extends Dispatch = Dispatch +>(config?: { + getFeatures?: (state: S) => Features[0]> | null | undefined + composeMiddleware?: ComposeMiddleware +}): Middleware diff --git a/src/featureReducersReducer.d.ts b/src/featureReducersReducer.d.ts index 4e76faf..82a96be 100644 --- a/src/featureReducersReducer.d.ts +++ b/src/featureReducersReducer.d.ts @@ -1,6 +1,9 @@ -import type { Reducer } from 'redux' +import type { AnyAction, Reducer } from 'redux' import type { Features, ComposeReducers } from './index' -export default function featureReducersReducer(config?: { - getFeatures?: (state: S) => Features | null | undefined - composeReducers?: ComposeReducers -}): Reducer +export default function featureReducersReducer< + S = any, + A extends AnyAction = AnyAction +>(config?: { + getFeatures?: (state: S) => Features | null | undefined + composeReducers?: ComposeReducers +}): Reducer diff --git a/src/featureStatesReducer.d.ts b/src/featureStatesReducer.d.ts index 3ce221f..b1b81b0 100644 --- a/src/featureStatesReducer.d.ts +++ b/src/featureStatesReducer.d.ts @@ -1,5 +1,6 @@ import type { Reducer } from 'redux' -import type { FeatureStates, CreateReducer } from './index' -export default function featureStatesReducer(config?: { - createReducer?: CreateReducer -}): Reducer +import type { FeatureStates, CreateReducer, FeatureAction } from './index' +export default function featureStatesReducer< + S extends FeatureStates = FeatureStates, + A extends FeatureAction = FeatureAction +>(config?: { createReducer?: CreateReducer }): Reducer diff --git a/src/featuresReducer.d.ts b/src/featuresReducer.d.ts index 33b4bbf..66da4ee 100644 --- a/src/featuresReducer.d.ts +++ b/src/featuresReducer.d.ts @@ -1,5 +1,9 @@ -import type { Reducer } from 'redux' +import type { AnyAction, Dispatch, Reducer } from 'redux' import type { Features, CreateReducer } from './index' -export default function featuresReducer(config?: { - createReducer?: CreateReducer> -}): Reducer> +export default function featuresReducer< + S = any, + A extends AnyAction = AnyAction, + D extends Dispatch = Dispatch +>(config?: { + createReducer?: CreateReducer> +}): Reducer> diff --git a/src/index.d.ts b/src/index.d.ts index 4ea1d8b..a1e96be 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -40,37 +40,47 @@ export { setFeatureState, loadInitialFeatures, } -import type { MiddlewareAPI, Reducer, Middleware, ActionCreator } from 'redux' -export type ActionCreators = Partial< - Record> -> -export interface CreateReducer { - ( - initialState: S, - reducers: { - [actionType: string]: Reducer - } - ): Reducer - (reducers: { [actionType: string]: Reducer }): Reducer +import type { + MiddlewareAPI, + Reducer, + Middleware, + AnyAction, + Dispatch, +} from 'redux' + +export interface CreateReducer { + (initialState: S, reducers: Record>): Reducer + (reducers: Record>): Reducer } -export type ComposeReducers = (...reducers: Array>) => Reducer -export type ComposeMiddleware = ( - ...middlewares: Array -) => Middleware +export type ComposeReducers = ( + ...reducers: Array> +) => Reducer +export type ComposeMiddleware< + DispatchExt = {}, + S = any, + D extends Dispatch = Dispatch +> = ( + ...middlewares: Array> +) => Middleware export type FeatureState = 'NOT_LOADED' | 'LOADING' | 'LOADED' | Error -export type FeatureStates = { - [featureId: string]: FeatureState -} -export type Feature = { - init?: (store: MiddlewareAPI) => any - load?: (store: MiddlewareAPI) => Promise> +export type FeatureStates = Record +export type Feature< + S = any, + A extends AnyAction = AnyAction, + D extends Dispatch = Dispatch +> = { + init?: (store: MiddlewareAPI) => any + load?: (store: MiddlewareAPI) => Promise> dependencies?: Array - middleware?: Middleware - reducer?: Reducer -} -export type Features = { - [featureId: string]: Feature + middleware?: Middleware + reducer?: Reducer } +export type Features< + S, + A extends AnyAction = AnyAction, + D extends Dispatch = Dispatch +> = Record> + export type FeatureAction = { type: string payload?: any diff --git a/src/loadFeatureMiddleware.d.ts b/src/loadFeatureMiddleware.d.ts index a082a02..c2cedf0 100644 --- a/src/loadFeatureMiddleware.d.ts +++ b/src/loadFeatureMiddleware.d.ts @@ -1,9 +1,15 @@ import type { Features, FeatureStates } from './index' -import type { Middleware } from 'redux' -export default function loadFeatureMiddleware(config?: { - getFeatures?: (state: S) => Features | null | undefined +import type { Dispatch, Middleware } from 'redux' +export default function loadFeatureMiddleware< + DispatchExt = {}, + S = any, + D extends Dispatch = Dispatch +>(config?: { + getFeatures?: ( + state: S + ) => Features[0], D> | null | undefined getFeatureStates?: (state: S) => FeatureStates | null | undefined - createMiddleware?: (middlewares: { - [actionType: string]: Middleware - }) => Middleware -}): Middleware + createMiddleware?: ( + middlewares: Record> + ) => Middleware +}): Middleware diff --git a/test/typecheck.ts b/test/typecheck.ts index 0dce97a..169620d 100644 --- a/test/typecheck.ts +++ b/test/typecheck.ts @@ -1,4 +1,9 @@ -import type { Features, FeatureStates, FeatureAction } from '../src/index' +import type { + Features, + FeatureStates, + FeatureAction, + Feature, +} from '../src/index' import { composeReducers, featuresReducer, @@ -19,22 +24,23 @@ type Action = } | FeatureAction type State = { - features: Features + features: Features featureStates: FeatureStates } -const reducer: Reducer = composeReducers( +const reducer: Reducer = composeReducers( combineReducers({ features: featuresReducer(), featureStates: featureStatesReducer(), }), featureReducersReducer() ) -const store: Store = createStore( +const store: Store = createStore( reducer, applyMiddleware(loadFeatureMiddleware(), featureMiddlewaresMiddleware()) ) -const feature = { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - reducer: (state: State | undefined, action: Action) => state, -} as const +const feature: Feature = { + // eslint-disable-next-line no-unused-vars + reducer: (state: State | undefined, action: Action) => + state || { features: {}, featureStates: {} }, +} store.dispatch(addFeature('test', feature)) diff --git a/tsconfig.json b/tsconfig.json index 580dea5..be00891 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,8 @@ { "extends": "./node_modules/@jcoreio/toolchain-typescript/tsconfig.json", "include": ["./src"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], + "compilerOptions": { + "skipLibCheck": false + } }