CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-typesafe-actions

Typesafe Action Creators for Redux / Flux Architectures in TypeScript

Pending
Overview
Eval results
Files

type-helpers.mddocs/

Type Helpers

Complete TypeScript type definitions and interfaces for building type-safe Redux applications with action creators, reducers, and state management utilities.

Capabilities

Core Types

Basic type definitions that form the foundation of the type system.

/**
 * Type representing action type constants (string literals)
 */
type TypeConstant = string;

/**
 * Generic action interface with type property
 */
interface Action<TType extends TypeConstant = TypeConstant> {
  type: TType;
}

/**
 * Generic action creator function type
 */
type ActionCreator<TAction extends Action = Action> = (
  ...args: any[]
) => TAction;

/**
 * Generic reducer function type
 */
type Reducer<TState, TAction extends Action> = (
  state: TState | undefined,
  action: TAction
) => TState;

Action Types

Specialized action types for different payload and meta combinations.

/**
 * Action without payload (type only)
 */
type EmptyAction<TType extends TypeConstant> = Action<TType>;

/**
 * Action with payload property
 */
interface PayloadAction<TType extends TypeConstant, TPayload> extends Action<TType> {
  payload: TPayload;
}

/**
 * Action with both payload and meta properties
 */
interface PayloadMetaAction<TType extends TypeConstant, TPayload, TMeta> 
  extends PayloadAction<TType, TPayload> {
  meta: TMeta;
}

Action Creator Types

Specialized action creator types corresponding to different action types.

/**
 * Action creator that produces EmptyAction
 */
type EmptyActionCreator<TType extends TypeConstant> = ActionCreator<EmptyAction<TType>>;

/**
 * Action creator that produces PayloadAction  
 */
type PayloadActionCreator<TType extends TypeConstant, TPayload> = 
  ActionCreator<PayloadAction<TType, TPayload>>;

/**
 * Action creator that produces PayloadMetaAction
 */
type PayloadMetaActionCreator<TType extends TypeConstant, TPayload, TMeta> = 
  ActionCreator<PayloadMetaAction<TType, TPayload, TMeta>>;

Type Metadata Interface

Interface for action creators with type metadata support.

/**
 * Interface representing type getter on action creator instance
 */
interface ActionCreatorTypeMetadata<TType extends TypeConstant> {
  getType?(): TType;
}

Utility Types

Advanced utility types for type inference and manipulation.

/**
 * Infers action union type from action creator or action creator map
 */
type ActionType<TActionCreatorOrMap extends any> = 
  TActionCreatorOrMap extends ActionCreator<TypeConstant>
    ? ReturnType<TActionCreatorOrMap>
    : TActionCreatorOrMap extends Record<any, any>
    ? { [K in keyof TActionCreatorOrMap]: ActionType<TActionCreatorOrMap[K]> }[keyof TActionCreatorOrMap]
    : never;

/**
 * Infers state object type from reducer or reducer map
 */
type StateType<TReducerOrMap extends any> = 
  TReducerOrMap extends Reducer<any, any>
    ? ReturnType<TReducerOrMap>
    : TReducerOrMap extends Record<any, any>
    ? { [K in keyof TReducerOrMap]: StateType<TReducerOrMap[K]> }
    : never;

Builder Types

Types used internally by action creator builders.

/**
 * Action builder type for createAction results
 */
type ActionBuilder<TType extends TypeConstant, TPayload = undefined, TMeta = undefined> =
  TPayload extends undefined
    ? TMeta extends undefined
      ? EmptyAction<TType>
      : never
    : TMeta extends undefined
    ? PayloadAction<TType, TPayload>
    : PayloadMetaAction<TType, TPayload, TMeta>;

/**
 * Action creator builder interface for createAction
 */
interface ActionCreatorBuilder<TType extends TypeConstant, TPayload = undefined, TMeta = undefined> {
  (): TPayload extends undefined
    ? TMeta extends undefined
      ? () => EmptyAction<TType>
      : never
    : never;

  <P>(): TMeta extends undefined
    ? (payload: P) => PayloadAction<TType, P>
    : never;

  <P, M>(): (payload: P, meta: M) => PayloadMetaAction<TType, P, M>;

  map<R, P>(
    fn: (payload: P) => R
  ): TPayload extends undefined
    ? ActionCreatorBuilder<TType, R, TMeta>
    : never;
}

/**
 * Async action creator builder for createAsyncAction
 */
interface AsyncActionCreatorBuilder<
  TRequestType extends TypeConstant,
  TSuccessType extends TypeConstant, 
  TFailureType extends TypeConstant,
  TCancelType extends TypeConstant = never
> {
  request: ActionCreatorBuilder<TRequestType>;
  success: ActionCreatorBuilder<TSuccessType>;
  failure: ActionCreatorBuilder<TFailureType>;
  cancel: TCancelType extends TypeConstant 
    ? ActionCreatorBuilder<TCancelType>
    : never;
}

Module Augmentation Interface

Interface for augmenting the library with custom types.

/**
 * Interface for module augmentation - extend this in your app
 * @example
 * ```
 * declare module 'typesafe-actions' {
 *   export type RootAction = ActionType<typeof import('./root-action').default>;
 *   export interface Types {
 *     RootAction: RootAction;
 *   }
 * }
 * ```
 */
interface Types {
  RootAction: Action;
}

Usage Examples

Basic Type Usage

import type { 
  Action, 
  ActionCreator, 
  PayloadAction, 
  EmptyAction 
} from "typesafe-actions";

// Define action types
type IncrementAction = PayloadAction<'INCREMENT', number>;
type DecrementAction = EmptyAction<'DECREMENT'>;
type CounterAction = IncrementAction | DecrementAction;

// Define action creators with proper typing
const increment: ActionCreator<IncrementAction> = (payload: number) => ({
  type: 'INCREMENT',
  payload,
});

const decrement: ActionCreator<DecrementAction> = () => ({
  type: 'DECREMENT',
});

ActionType and StateType Usage

import { createAction, createReducer } from "typesafe-actions";
import type { ActionType, StateType } from "typesafe-actions";

// Create action creators
const counterActions = {
  increment: createAction('INCREMENT')<number>(),
  decrement: createAction('DECREMENT')(),
  reset: createAction('RESET')(),
};

// Infer action union type
type CounterAction = ActionType<typeof counterActions>;
// Result: ReturnType<typeof increment> | ReturnType<typeof decrement> | ReturnType<typeof reset>

// Create reducers
const rootReducer = {
  counter: createReducer({ count: 0 })
    .handleAction(counterActions.increment, (state, action) => ({
      count: state.count + action.payload,
    }))
    .handleAction(counterActions.decrement, (state) => ({
      count: state.count - 1,
    }))
    .handleAction(counterActions.reset, () => ({ count: 0 })),
  
  user: createReducer({ name: '', email: '' })
    .handleAction(setUser, (state, action) => action.payload),
};

// Infer root state type
type RootState = StateType<typeof rootReducer>;
// Result: { counter: { count: number }, user: { name: string, email: string } }

Module Augmentation

// In your application's type definitions file
import { ActionType } from 'typesafe-actions';

declare module 'typesafe-actions' {
  interface Types {
    RootAction: ActionType<typeof rootActions>;
  }
}

// Now you can use RootAction throughout your app
import type { RootAction } from 'typesafe-actions';

function handleAction(action: RootAction) {
  // action is typed as your app's root action union
}

Advanced Generic Usage

import type { 
  ActionCreatorBuilder, 
  AsyncActionCreatorBuilder,
  ActionCreatorTypeMetadata 
} from "typesafe-actions";

// Generic action creator factory
function createTypedAction<T extends string>(type: T) {
  return <P = undefined, M = undefined>(): ActionCreatorBuilder<T, P, M> => {
    // Implementation would go here
    return {} as ActionCreatorBuilder<T, P, M>;
  };
}

// Generic async action factory
function createTypedAsyncAction<
  R extends string,
  S extends string,
  F extends string
>(requestType: R, successType: S, failureType: F) {
  return <RP = undefined, SP = undefined, FP = undefined>(): 
    AsyncActionCreatorBuilder<R, S, F> => {
    // Implementation would go here
    return {} as AsyncActionCreatorBuilder<R, S, F>;
  };
}

// Usage with full type inference
const typedAction = createTypedAction('TYPED_ACTION')<string, number>();
const result = typedAction('payload', 42);
// result is typed as PayloadMetaAction<'TYPED_ACTION', string, number>

Type Guards and Narrowing

import type { Action, PayloadAction, EmptyAction } from "typesafe-actions";

// Custom type guards
function isPayloadAction<T extends string, P>(
  action: Action,
  type: T
): action is PayloadAction<T, P> {
  return action.type === type && 'payload' in action;
}

function isEmptyAction<T extends string>(
  action: Action,
  type: T
): action is EmptyAction<T> {
  return action.type === type && !('payload' in action);
}

// Usage in reducers
function counterReducer(state = { count: 0 }, action: Action) {
  if (isPayloadAction<'INCREMENT', number>(action, 'INCREMENT')) {
    // TypeScript knows action.payload is number
    return { count: state.count + action.payload };
  }
  
  if (isEmptyAction(action, 'DECREMENT')) {
    // TypeScript knows action has no payload
    return { count: state.count - 1 };
  }
  
  return state;
}

Internal Utility Types

These types are used internally by the library but may be useful for advanced usage:

/**
 * Internal type for resolving types - ensures proper type resolution in action creators
 */
type ResolveType<T> = T extends (...args: any[]) => any ? ReturnType<T> : T;

/**
 * Internal utility for excluding never types from unions
 */
type ExcludeNever<T> = T extends never ? never : T;

Install with Tessl CLI

npx tessl i tessl/npm-typesafe-actions

docs

action-creators.md

action-helpers.md

index.md

type-helpers.md

tile.json