type Action = { type: string }

export type ActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState) => R

type AsyncAcceptingDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: ActionThunk<R>): R;
  <R, A extends Action>(thunk: A | ActionThunk<R>): A | R;
}

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R

type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)

type BoundRegistryActionCreator<F extends RegistryActionCreator<unknown>> =
    ReturnType<F> extends RegistryActionThunk<unknown>
        ? (...args: Parameters<F>) => ReturnType<ReturnType<F>>
        : F

type RegistryActionCreatorsMapObject = { [key: string]: RegistryActionCreator<unknown> }

type BoundRegistryActionCreatorsMapObject<TActionCreators extends RegistryActionCreatorsMapObject> = {
  [TActionCreatorName in keyof TActionCreators]: BoundRegistryActionCreator<TActionCreators[TActionCreatorName]>
};

type RegistryDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: RegistryActionThunk<R>): R;
  <R, A extends Action>(thunk: A | RegistryActionThunk<R>): A | R;
}

Types from actionUtils.ts

The basic stuff

type Action = { type: string }

export type ActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState) => R

type AsyncAcceptingDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: ActionThunk<R>): R;
  <R, A extends Action>(thunk: A | ActionThunk<R>): A | R;
}

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R
type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)
type BoundRegistryActionCreator<F extends RegistryActionCreator<unknown>> =
    ReturnType<F> extends RegistryActionThunk<unknown>
        ? (...args: Parameters<F>) => ReturnType<ReturnType<F>>
        : F
type RegistryActionCreatorsMapObject = { [key: string]: RegistryActionCreator<unknown> }
type BoundRegistryActionCreatorsMapObject<TActionCreators extends RegistryActionCreatorsMapObject> = {
  [TActionCreatorName in keyof TActionCreators]: BoundRegistryActionCreator<TActionCreators[TActionCreatorName]>
};
type RegistryDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: RegistryActionThunk<R>): R;
  <R, A extends Action>(thunk: A | RegistryActionThunk<R>): A | R;
}

Overloads

type Action = { type: string }
export type ActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState) => R

type AsyncAcceptingDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: ActionThunk<R>): R;
  <R, A extends Action>(thunk: A | ActionThunk<R>): A | R;
}

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R
type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)
type BoundRegistryActionCreator<F extends RegistryActionCreator<unknown>> =
    ReturnType<F> extends RegistryActionThunk<unknown>
        ? (...args: Parameters<F>) => ReturnType<ReturnType<F>>
        : F
type RegistryActionCreatorsMapObject = { [key: string]: RegistryActionCreator<unknown> }
type BoundRegistryActionCreatorsMapObject<TActionCreators extends RegistryActionCreatorsMapObject> = {
  [TActionCreatorName in keyof TActionCreators]: BoundRegistryActionCreator<TActionCreators[TActionCreatorName]>
};
type RegistryDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: RegistryActionThunk<R>): R;
  <R, A extends Action>(thunk: A | RegistryActionThunk<R>): A | R;
}
type Adder = {
  (a: number, b: number): number;
}

type Adder2 = (a: number, b: number) => number;


namespace React {
  interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>, context?: any): ReactElement | null;
    propTypes?: WeakValidationMap<P>;
    contextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
  }
}

Function objects

Overloads

function flip(n: number): number;
function flip(s: string): string;
function flip(x: number | string): number | string {
  if (typeof x == "number") {
    return -x;
  }
  return x;
}

type FlipType = {
  (n: number): number;
  (s: string): string;
  (x: number | string): number | string
};

Task: Define the type for a function that returns string when called with a number and a number when called with a string

Registry stuff

type Action = { type: string }
export type ActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState) => R
type AsyncAcceptingDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: ActionThunk<R>): R;
  <R, A extends Action>(thunk: A | ActionThunk<R>): A | R;
}

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R

type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)

type BoundRegistryActionCreator<F extends RegistryActionCreator<unknown>> =
    ReturnType<F> extends RegistryActionThunk<unknown>
        ? (...args: Parameters<F>) => ReturnType<ReturnType<F>>
        : F
type RegistryActionCreatorsMapObject = { [key: string]: RegistryActionCreator<unknown> }
type BoundRegistryActionCreatorsMapObject<TActionCreators extends RegistryActionCreatorsMapObject> = {
  [TActionCreatorName in keyof TActionCreators]: BoundRegistryActionCreator<TActionCreators[TActionCreatorName]>
};
type RegistryDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: RegistryActionThunk<R>): R;
  <R, A extends Action>(thunk: A | RegistryActionThunk<R>): A | R;
}

Increasing complexity



type ActionCreator<R> = (...args: any[]) => Action

type ReduxThunkActionCreator<R> = (...args: any[]) => (Action | ActionThunk<R>)

type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)

Why export?

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R
export function doSomething(/* ... */): RegistryActionThunk<Promise<ResourceState>> {
  return somethingBeak({
    // ...
  });
}

Conditional Types

type Action = { type: string }
export type ActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState) => R
type AsyncAcceptingDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: ActionThunk<R>): R;
  <R, A extends Action>(thunk: A | ActionThunk<R>): A | R;
}

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R
type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)
type BoundRegistryActionCreator<F extends RegistryActionCreator<unknown>> =
    ReturnType<F> extends RegistryActionThunk<unknown>
        ? (...args: Parameters<F>) => ReturnType<ReturnType<F>>
        : F
type RegistryActionCreatorsMapObject = { [key: string]: RegistryActionCreator<unknown> }
type BoundRegistryActionCreatorsMapObject<TActionCreators extends RegistryActionCreatorsMapObject> = {
  [TActionCreatorName in keyof TActionCreators]: BoundRegistryActionCreator<TActionCreators[TActionCreatorName]>
};
type RegistryDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: RegistryActionThunk<R>): R;
  <R, A extends Action>(thunk: A | RegistryActionThunk<R>): A | R;
}
T extends U ? X : Y

Index types and mapped types

type Action = { type: string }
export type ActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState) => R
type AsyncAcceptingDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: ActionThunk<R>): R;
  <R, A extends Action>(thunk: A | ActionThunk<R>): A | R;
}

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R
type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)
type BoundRegistryActionCreator<F extends RegistryActionCreator<unknown>> =
    ReturnType<F> extends RegistryActionThunk<unknown>
        ? (...args: Parameters<F>) => ReturnType<ReturnType<F>>
        : F
type RegistryActionCreatorsMapObject = { [key: string]: RegistryActionCreator<unknown> }
type BoundRegistryActionCreatorsMapObject<TActionCreators extends RegistryActionCreatorsMapObject> = {
  [TActionCreatorName in keyof TActionCreators]: BoundRegistryActionCreator<TActionCreators[TActionCreatorName]>
};
type RegistryDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: RegistryActionThunk<R>): R;
  <R, A extends Action>(thunk: A | RegistryActionThunk<R>): A | R;
}

Index types and mapped types

type Names = { [key: string]: string };



function weird3<T extends {}>(arrs: T): { [K in keyof T]: number } {
  // ...
}

weird3({ foo: "bar" }).foo
type Action = { type: string }

export type ActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState) => R

type AsyncAcceptingDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: ActionThunk<R>): R;
  <R, A extends Action>(thunk: A | ActionThunk<R>): A | R;
}

export type RegistryActionThunk<R> =
  (dispatch: AsyncAcceptingDispatch, getState: () => ReduxState, resourceRegistryId: string) => R

type RegistryActionCreator<R> = (...args: any[]) => (Action | RegistryActionThunk<R>)

type BoundRegistryActionCreator<F extends RegistryActionCreator<unknown>> =
    ReturnType<F> extends RegistryActionThunk<unknown>
        ? (...args: Parameters<F>) => ReturnType<ReturnType<F>>
        : F

type RegistryActionCreatorsMapObject = { [key: string]: RegistryActionCreator<unknown> }

type BoundRegistryActionCreatorsMapObject<TActionCreators extends RegistryActionCreatorsMapObject> = {
  [TActionCreatorName in keyof TActionCreators]: BoundRegistryActionCreator<TActionCreators[TActionCreatorName]>
};

type RegistryDispatch = {
  <A extends Action>(action: A): A;
  <R>(thunk: RegistryActionThunk<R>): R;
  <R, A extends Action>(thunk: A | RegistryActionThunk<R>): A | R;
}

Understanding actionUtils.ts

By Erik Vesteraas

Understanding actionUtils.ts

  • 651