[英]Variadic generic function accepting generic types as input arguments
[英]How to validate generic variadic mapped tuple arguments without losing the inferred generic types?
我有一個高階函數createHandler
,它以可變映射元組的形式接受剩余參數,將它們映射到通用對象類型(我們現在稱它為ObjA<>
)。 然后可以將返回的函數傳遞給另一個高階函數useHandler
,該函數將調用它(顯然在此過程中做更多事情)。
我斷言從createHandler
返回的函數類型是一種特定類型(只是將它與“品牌”相交,即BrandedValidHandler
),因此只有useHandler
可以接受並調用它。
在接受它作為有效輸入之前,我想對createHandler
的參數數組執行一些驗證。 具體來說,我想檢查ObjA<>
字段中的重復字符串,如果有重復則拒絕輸入。 我遇到的問題是在createHandler
的參數數組上執行重復項檢查,而不會丟失泛型類型推斷(特別是一些很棒的上下文類型)。
有沒有辦法在不丟失此推論的情況下對輸入執行此檢查?
我想出的解決方案是改為驗證createHandler
的返回類型中的輸入,並返回某種類型的錯誤而不是BrandedValidHandler
。 期望是createHandler
和useHandler
將僅相互結合使用,因此這“有效”,但最好是輸入本身無效,而不是返回類型。 這是一個最小的、相當人為的、帶注釋的示例(我實際上並沒有檢查User
對象中的重復名稱,但重點是這樣的):
type User<Name extends string, Email extends string> = {
name: Name;
email: Email;
// The handler can use the exact name and email provided above
handler: (name: Name, email: Email) => void;
};
// Homomorphic mapped tuple allows us infer Names and Emails
type MapUsers<Names extends string[], Emails extends string[]> =
(
{
[K in keyof Names]: User<Names[K], K extends keyof Emails ? Emails[K] : never>
}
&
{
[K in keyof Emails]: User<K extends keyof Names ? Names[K] : never, Emails[K]>
}
);
// a type describing a valid creation of a `createUsers` handler
type ValidUsersFunc = (eventType: 'a' | 'b') => Promise<string> & { __brand: 'ValidUsersFunc' };
// check for duplicate names amongst any of the User objects
type IsUniqueUsersNames<Users extends readonly User<any, any>[]> =
Users extends readonly [infer U extends User<any, any>, ...infer Rest extends User<any, any>[]]
? U['name'] extends Rest[number]['name']
? Error & { reason: `Duplicate name: '${U['name']}'` }
: IsUniqueUsersNames<Rest>
: ValidUsersFunc;
// Higher order function to return function that can be called by `useUsers`
const createUsers = <Names extends string[], Emails extends string[]>(...users: MapUsers<Names, Emails>) =>
(async (eventType: 'a' | 'b') => {
console.log(eventType);
users.forEach(console.log)
return "";
}) as unknown as IsUniqueUsersNames<(typeof users)>; // pretty cool that we can use (typeof users) here though, I might add!
const users = createUsers(
{
name: 'conor',
email: 'conor@example.com',
handler: (name, email) => {
name; // contextually typed as 'conor'
email; // contextually typed as 'conor@example.com'
}
},
{
name: 'joe',
email: 'joe@example.com',
handler: (name, email) => {
name;
email;
}
},
{
name: 'conor', // uh oh, can't have 'conor' twice
email: 'conor@google.com',
handler: (name, email) => {
name;
email;
}
}
);
const useUsers = async (users: ValidUsersFunc) => {
return await users('a');
};
// ERROR, which is good!
// Argument of type 'Error & { reason: "Duplicate name: 'conor'"; }' is not assignable to parameter of type 'ValidUsersFunc'.
useUsers(users);
相反,我想做的是使用我在MapUsers
中創建的映射類型,並在那里執行重復檢查。 然而,這樣做會失去通用推理:
type MapUsers<Names extends string[], Emails extends string[]> =
(
{
[K in keyof Names]: User<Names[K], K extends keyof Emails ? Emails[K] : never>
}
&
{
[K in keyof Emails]: User<K extends keyof Names ? Names[K] : never, Emails[K]>
}
) extends (infer users extends User<any, any>[] // Uh oh, `any`!
// This creates an issue, using `any` loses all type inference gained above in the mapped tuple intersection (I think).
? IsUniqueUsersNames<users> extends true // note: would be a different implementation of `IsUniqueUsersNames`, just returns a boolean instead
? users
: Error
: never;
嘗試 2: extends infer users extends any[]
:丟失上下文類型
嘗試 3: extends infer users
:可以毫無怨言地創建一個新的映射類型,但是隨后...users
在createUsers
中不被識別為數組。
嘗試 4 抓住救命稻草:失去上下文打字
extends infer users extends User<any, any>[]
? {
[K in keyof users]: users[K] extends User<infer N, infer E> ? User<N, E> : never
}
我正在尋找的是一種在infer users
行中“存儲”或“記住”泛型的方法,但我不確定這是否可行。 顯然, User
類型不能獨立存在。
一般來說,我會用
import {F} from 'ts-toolbelt'
function validate<T extends any[]>(
...args: T extends Validate<T> ? T : F.NoInfer<Validate<T>>
): void {}
type Validate<T extends any[]> = Unique<T>
其中 Validate 通過將錯誤值替換為[..., Error, ...]
、 [..., never, ...]
或其他任何內容來使類型或多或少正確
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.