簡體   English   中英

TypeScript 在咖喱中使用通用 function 參數 function

[英]TypeScript use generic function parameter in curry function

我正在嘗試創建一個 function 來生成我的 redux 動作創建者。
假設我們的操作有以下類型:

export type DeleteAction = {
  type: typeof BLAH_DELETE;
  payload: {
    id: string;
  };
};

export type EditAction = {
  type: typeof BLAH_EDIT;
  payload: {
    id: string;
    name: string;
  };
};

export MyAction = DeleteAction | EditAction

現在在動作文件中,我想以這種方式創建我的動作:

export const deleteBlah = makeActionCreator<MyAction>('BLAH_DELETE');

// Expected Behaviour 
deleteBlah({ id: '' }) // Correct
deleteBlah({ id: '', name: '' }) // Error

export const editBlah = makeActionCreator<MyAction>('BLAH_EDIT');

// Expected Behaviour 
editBlah({ id: '', name: '' }) // Correct
editBlah({ id: '' }) // Error

這是makeActionCreator function:

export const makeActionCreator = <A extends { type: string; payload: any }>(type: A['type']) => (
  payload: ActionPayload<ExtractAction<A, A['type']>>,
) => ({
  type,
  payload,
});

type ExtractAction<A, T> = A extends { type: T } ? A : never;

type ActionPayload<T extends { payload: any }> = Pick<T['payload'], keyof T['payload']>;

問題是我不知道如何將操作文件中提供的操作類型傳遞給ExtractAction<A, A['type']>因此,有效負載對於A的所有可能選項始終有效。

最終,我有一些想法可以將您的想法付諸實踐。 以下是幾個步驟:

  • 首先,我們定義了一些 utils 類型,我們可以檢測任何 prop 的類型並提取payload屬性的類型:
type ValueType<T, K> = K extends keyof T ? T[K] : never;

type ExtractPayload<A, T> = A extends { type: T, payload: infer R } ? R : never;

  • 接下來,我們定義 return Curry function 接收動作作為參數:
type Curry<A> = <T extends ValueType<A, 'type'>>(arg: T) => (payload: ExtractPayload<A, T>) => {
  type: T
  payload: ExtractPayload<A, T>
};

  • 最后,我們重新編寫了您的動作創建器 function,因為我不知道如何聲明 function 的類型,而不僅僅是定義返回類型,因此您必須再創建一層咖喱makeActionCreator<MyAction>()這有點煩人:
export const makeActionCreator = <A>(): Curry<A> => action => payload => ({
  type: action,
  payload,
})

// Testing

const deleteBlah = makeActionCreator<MyAction>()('BLAH_DELETE');
deleteBlah({ id: '' }) // Correct
deleteBlah({ id: '', name: '' }) // Error

另一種解決方案

另一種選擇,您將保留您的 function 而不創建更多咖喱級別,但您必須再傳遞一個輸入參數,如下所示:

type Curry1<A, T> = (payload: ExtractPayload<A, T>) => {
  type: T
  payload: ExtractPayload<A, T>
};

// The function is the same but have type T as new parameter

export const makeActionCreator = <A, T>(action: T): Curry1<A, T> => payload => ({
  type: action,
  payload,
})

// Testing, it's a bit odd as specify 'BLAH_DELETE' twice

const deleteBlah = makeActionCreator<MyAction, 'BLAH_DELETE'>('BLAH_DELETE');
deleteBlah({ id: '' }) // Correct
deleteBlah({ id: '', name: '' }) // Error

type Some<U, I> = (U extends I ? true : false) extends false ? false : true;

function makeCreator<T extends MyAction["type"]>(type: T) {
  return function <P>(
    p: { type: T; payload: P } extends MyAction
      ? Some<MyAction, { type: T; payload: P }> extends true
        ? P
        : never
      : never
  ): MyAction {
    return null as any;
  };
}

代碼

 { type: T; payload: P } extends MyAction
      ? OneOf<MyAction, { type: T; payload: P }> extends true
        ? P
        : never
      : never`

斷言您提供的參數正是根據給定type的有效負載類型

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM