簡體   English   中英

派生類型推斷為 any

[英]Derived type inferred as any

在這里,我希望能夠將 api 推斷為{ login: (param: number) => Promise<void> } ,但我得到了任何結果。

export function createSlice<
  T extends Record<
    keyof T,
    (state: S, payload?: any) => S | ((api: {
      [K in keyof T]: Parameters<T[K]>[1] extends undefined
        ? { (): void }
        : { (payload: Parameters<T[K]>[1]): void }
    }) => Promise<void>)
  >,
  N extends string,
  S,
>(
  config: T, name: N, initialState: S
) {
  return {};
}

interface TestState {
  isTest: boolean;
}

const slice = createSlice({
  login: (state: TestState, param: number) => async (api) => {
    api.login('sdf');
  },
}, 'testSlice', { isTest: false });

這是一個基本的限制嗎? 如果是這樣,我怎樣才能重新工作 API 以實現我想要的?

操場

從我所看到的一切來看,通常不可能從context獲得泛型類型參數和回調參數類型的同時推斷。 雖然人類可能很容易直覺這種推斷必須成功的正確順序(在您的情況下,從config參數中方法的鍵和第二個參數推斷T類型參數,然后推斷從這些方法返回的回調的參數),編譯器並不能真正做到這一點。 它的算法在很多情況下都能很好地工作,但在其他情況下卻失敗了。 GitHub 中有各種關於這類事情的問題; 例如,請參閱microsoft/TypeScript#44999 大多數情況下,這些都是作為設計限制而關閉的,並帶有返回microsoft/TypeScript#30134的鏈接,這是一個實現完整統一算法的建議,該算法可能會在很長一段時間(或永遠)保持未實現的建議,因為如果沒有這將是一項艱巨的任務明顯提高的機會。

目前,這意味着您將需要通過手動注釋您不想注釋的回調參數,或者通過手動指定您不希望指定的泛型類型參數來解決問題。 我認為后者可能是更好的方法,特別是如果我們重構以使您指定的類型盡可能簡潔。 這是一種方法:

declare const createSlice: <T extends object>() =>
  <N extends string, S>(
    config: { [K in keyof T]:
      (state: S, payload: T[K]) => S | (
        (api: { [P in keyof T]: (payload: T[P]) => void }) => Promise<void>
      ) },
    name: N,
    initialState: S
  ) => void;

這是一個curried function,它可以解決缺少microsoft/TypeScript#26242中要求的部分類型參數推斷的問題。 我們將不得不手動指定T ,但我們仍然希望編譯器推斷NS 這不能在單個通用 function 中完成,因此我將其拆分,以便您可以指定T ,然后返回的 function 將為您推斷NS 就是這樣。

我還重構了T使其只是從鍵直接映射到第二種方法參數類型。 這種類型看起來像相對簡單的{login: number}而不是更復雜的{login: (s: S, p: number) => S |...對應於config參數。 相反,我們從更簡單的Tmap 開始config的類型。


好的,讓我們測試一下:

const slice = createSlice<{
  login: number,
  loginSuccess: void,
}>()({
  loginSuccess: (state) => state,
  login: (state, param) => async (api) => {
    api.login(123); // <--  okay
    api.login('sdf'); // <-- error
    api.loginSuccess(); // <-- okay
  },
}, 'testSlice', { isTest: false });

看起來不錯。 createSlice<{login: number, loginSuccess: void}>()的結果是類型

<N extends string, S>(config: {
    login: (state: S, payload: number) => S | ((api: {
        login: (payload: number) => void;
        loginSuccess: (payload: void) => void;
    }) => Promise<void>);
    loginSuccess: (state: S, payload: void) => S | ((api: {
        login: (payload: number) => void;
        loginSuccess: (payload: void) => void;
    }) => Promise<void>);
}, name: N, initialState: S) => void

當您使用上述參數調用它時,編譯器可以輕松推斷N的類型為"testSlice"S的類型為{isTest: boolean} ,並且未注釋的api回調參數的類型為{login: (payload: number)=>void; loginSuccess: (payload: void) => void} {login: (payload: number)=>void; loginSuccess: (payload: void) => void}

這與我能想到的你在這里想要的差不多。

Playground 代碼鏈接

暫無
暫無

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

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