[英]Can I infer types from input arguments?
可以從選項對象推斷類型嗎?
這是我人為的例子。
這里的想法是我有一個接受對象的函數,其中字段是轉換函數(將 A 轉換為 B 等)。 這個函數的返回是一個功能相似的對象,只是返回類型略有不同。
我已經放入了一些泛型,但我不確定如何使其工作。 我在 Redux Toolkit 的createSlice()
函數中看到了類似的設計,但它非常復雜。
type InputActionFn = (arg: any) => any;
interface Options {
actions: {
[K: string]: InputActionFn;
};
}
interface ActionPayload<B> {
payload: B;
}
type OutputActionFn<A, B> = (arg: A) => ActionPayload<B>;
interface ConverterReturn {
actions: {
[K: string]: OutputActionFn<any, any>;
};
}
export function createConvertor(options: Options): ConverterReturn {
const actions: Record<string, OutputActionFn<any, any>> = {};
// For each key I create a function that returns my converted payload.
Object.keys(options.actions).forEach(key => {
actions[key] = arg => {
return {
payload: options.actions[key](arg),
};
};
});
return {
actions,
};
}
這就是我計划使用它的方式:
// I can specify as many converter functions here as I want
const conv = createConvertor({
actions: {
actionA: (a: number) => a.toFixed(),
actionB: (b: string) => parseInt(b, 10),
},
});
// actionA should be typed properly like this:
// actionA(arg: number) => ActionPayload<string>
const resultA = conv.actions.actionA(5);
console.log(resultA.payload);
// Outputs: "5"
const resultB = conv.actions.actionB('10');
console.log(resultB.payload);
// Outputs: 10
可以使用打字稿來推斷類型
就是這樣:
type InputActionFn = (arg: any) => any;
interface Options {
[K: string]: InputActionFn;
}
interface ActionPayload<B> {
payload: B;
}
type OutputActionFn<A, B> = (arg: A) => ActionPayload<B>;
type Actions<T extends Options> = {
[key in keyof T]: OutputActionFn<Parameters<T[key]>[0], ReturnType<T[key]>>;
};
export function createConvertor<T extends Options = any>(options: T) {
const actions = {} as Actions<T>;
// For each key I create a function that returns my converted payload.
Object.keys(options).forEach((key) => {
actions[key] = (arg: Parameters<T[keyof T]>[0]) => {
return {
payload: options[key](arg)
};
};
});
return {
actions
};
}
const conv = createConvertor({
actionA: (a: number) => a.toFixed(),
actionB: (b: string) => parseInt(b, 10)
});
// Action A is hinted as (arg: number) => ActionPayload<string>
const resultA = conv.actions.actionA(5);
console.log(resultA.payload);
// Outputs: "5"
// Action B is hinted as (arg: string) => ActionPayload<number>
const resultB = conv.actions.actionB('10');
console.log(resultB.payload);
// Outputs: 10
導入部分在這里
type Actions<T extends Options> = {
[key in keyof T]: OutputActionFn<Parameters<T[key]>[0], ReturnType<T[key]>>;
};
我們告訴 TypeScript 將 Actions 類型為一個對象,它的鍵在泛型 T 的 keyof 中。對於每個鍵,我們指定屬性類型是 OutputActionFn 並將相應的類型傳遞給泛型。 Parameters<T>
實用程序類型幫助我們推斷您傳遞的輸入函數的參數類型(請參閱https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype )。 由於只有一個預期參數,我們選擇Parameters<T>[0]
作為我們的參數類型。 然后我們使用另一個實用程序類型ReturnType<T>
(參見https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype ),它推斷我們傳遞的輸入函數的返回類型。
唯一的問題是 ESLint 會抱怨這個。 可能不得不忽略這一點。
// TS2536: Type 'string' cannot be used to index type 'Actions '.
actions[key] = (arg: Parameters<T[keyof T]>[0]) => {
return {
payload: options[key](arg)
};
};
編輯:我差點忘了。 這當然也是一個非常重要的細節,不容忽視。
export function createConvertor<T extends Options = any>(options: T) {
傳遞給 createConvertor 函數的泛型 Type 應用於函數的 options 參數。 我們不使用 Options 接口,它不會返回預期的類型。 盡管 options 仍然被輸入為類型 Options 的參數,因為我們從 Options 擴展了泛型類型 T。
使用幾個 TypeScript 通用實用程序( in
、 Parameters
、 ReturnType
等),您可以非常靈活地轉換對象類型。
這是您的代碼示例的工作示例:
type Payloader<T extends Record<string, any>> = {
[Key in keyof T]: (...params: Parameters<T[Key]>) => ({
payload: ReturnType<T[Key]>
})
}
function createConvertor<Actions extends Record<string, any>>(options: { actions: Actions }) {
const actions: Payloader<Actions> = {} as any;
Object.keys(options.actions).forEach((key) =>
// @ts-ignore
actions[key] = ((...args) => ({
payload: options.actions[key](...args),
})
))
return { actions }
}
const conv = createConvertor({
actions: {
actionA: (a: number) => a.toFixed(),
actionB: (b: string) => parseInt(b, 10)
},
})
const resultA = conv.actions.actionA(5);
console.log(resultA.payload);
// Outputs: "5"
const resultB = conv.actions.actionB('10');
console.log(resultB.payload);
// Outputs: 10
* 注意:函數實現使用@ts-ignore
,假設我們不需要正確鍵入實現。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.