簡體   English   中英

組合通用 function 參數類型

[英]Combining generic function argument types

我希望 typescript 接受具有相似結構但具有不同值的 arguments 列表,並正確推斷生成的聯合類型。

示例代碼

interface User<
    N extends string = string,
    S extends unknown = unknown,
    A extends (a: S) => unknown = (a: S) => unknown
> {
    name: N;
    state: S;
    action: A;
}

function createUser<
    N extends string = string,
    S extends unknown = unknown,
    A extends (a: S) => unknown = (a: S) => unknown
>(name: N, state: S, action: A): User<N, S, A> {
    return { action, name, state };
}

function combineUsers<
    V extends User<string, unknown, (a: unknown) => unknown>[]
>(...users: V) {
    type Names = V[number]["name"];
    return {
        actions: users.reduce(
            (a, u) => ({ ...a, [u.name]: u.action }),
            {} as { [K in Names]: Extract<V[number], { name: K }>["action"] },
        ),
        names: new Set<Names>(users.map((u) => u.name)),
    };
}

const userA = createUser("user1", 1, (_a: number) => undefined);
const user2 = createUser("userB", "two", (_s: string) => true);
const users = combineUsers(userA, user2);
// Argument of type 'User<"user1", number, (_a: number) => undefined>' is not assignable to parameter of type 'User<string, unknown, (a: unknown) => unknown>'.
//   Type '(_a: number) => undefined' is not assignable to type '(a: unknown) => unknown'.
//     Types of parameters '_a' and 'a' are incompatible.
//       Type 'unknown' is not assignable to type 'number'.ts(2345)

那是由於不兼容的功能而導致的編譯錯誤。

我希望看到 function 返回的類型是某種推斷的聯合;

type Return = {
    names: Set<"user1" | "userB">,
    actions: {
        user1: (_a: number) => undefined;
        userB: (_s: string) => boolean;
    }
}
  • 這可能嗎? 可以灑any但我寧願不
  • 是否有任何替代方法或更好的方法來實現這一目標

編輯:

@jcalz 的解決方案對於之前版本中所述的問題非常有效,但我的示例缺少一些使其適用於我的實際代碼的內容,即當 function 的參數必須與別處定義的類型匹配時。

我給combinedUsers的輸入看起來像這樣:

function combineUsers<V extends User<string, never[], unknown>[]>(...users: V) {
  type Names = V[number]['name'];
  return {
    actions: users.reduce(
      (a, u) => ({ ...a, [u.name]: u.action }),
      {} as { [K in Names]: Extract<V[number], { name: K }>['action'] }
    ),
    names: new Set<Names>(users.map((u) => u.name)),
  };
}

請注意,只有一個泛型類型參數V對應於傳入的User<N, A, R>對象數組。類型參數string (屬性類型的協變上限約束), never[] (逆變下限約束)對於參數類型)和unknown (返回值類型的協變上限約束)比anyanyany稍微更安全一些,但仍然可以允許一些奇怪的輸入。 希望這實際上並不重要。

在實現中,我們定義Names類型來獲取V的每個元素的N個參數的聯合( V[number]['name']是您使用number索引索引users然后索引結果User時獲得的類型帶有"name"索引)。

actions屬性的類型是一個映射類型,其中對於Names中的每個名稱,我們找到V中以該名稱作為name的元素,並獲取其action類型。


讓我們看看它是否有效。 假設您有這些類型的userAuser2

//const userA: User<"user1", [x: number, y: string], undefined>
//const user2: User<"userB", [x: string], boolean>

然后你combineUsers(userA, user2)結果是:

const users = combineUsers(userA, user2);
/* const users: {
    actions: {
        user1: (x: number, y: string) => undefined;
        userB: (x: string) => boolean;
    };
    names: Set<"user1" | "userB">;
} */

看起來不錯。

游樂場代碼鏈接

暫無
暫無

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

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