[英]Infer return type based on variadic argument type
我正在構建一個包含大量命令和選項的大型 CLI 應用程序,其中一些在命令之間重用。 我正在使用yargs
庫,我發現自己創建了如下所示的輔助函數:
const withFoo = (yargs: Argv) =>
yargs.option('foo', {
type: 'string',
});
const withBar = (yargs: Argv) =>
yargs.option('bar', {
type: 'string',
});
他們像這樣使用它們:
const bazOptionsBuilder = (yargs: Argv) =>
withFoo(withBar(yargs)).option('baz', { type: 'string' });
現在,我想創建一個輔助函數來減少嵌套,我可以這樣做:
const bazOptionsBuilder = withOptions(
withFoo,
withBar,
/* ... possibly more here */
(yargs) => yargs.option('baz', { type: 'string' }),
);
實現withOptions
函數相對簡單,但我正在努力輸入它。 到目前為止,我想出了:
import { Argv } from 'yargs';
type YargsBuilder<A extends Argv> = (y: Argv) => A;
type ArgsOf<T> = T extends Argv<infer A> ? A : never;
export function withOptions<A extends Argv<any>, B extends Argv<any>>(
a: YargsBuilder<A>,
b: YargsBuilder<B>,
): YargsBuilder<Argv<ArgsOf<A> & ArgsOf<B>>>;
export function withOptions<A extends Argv<any>, B extends Argv<any>, C extends Argv<any>>(
a: YargsBuilder<A>,
b: YargsBuilder<B>,
c: YargsBuilder<C>,
): YargsBuilder<Argv<ArgsOf<A> & ArgsOf<B> & ArgsOf<C>>>;
// ... and more ...
但這迫使我為withOptions
任意數量的參數withOptions
。 是否可以為withOptions
創建一個單一的類型簽名,它會告訴withOptions
為YargsBuilder
s 中傳遞的任意數量,返回類型將是所有這些類型的聯合?
為什么不將所有選項作為配置對象傳遞?
不同的語言支持不同的工具來提供可選參數,雖然在其他一些語言中構建器可能是你唯一的選擇,但配置對象在 JS 中是更慣用的方法,yargs 也支持它:
可選的 .options() 可以采用將鍵映射到 opt 參數的對象。
var argv = require('yargs') .options({ 'f': { alias: 'file', demandOption: true, default: '/etc/passwd', describe: 'x marks the spot', type: 'string' } }) .argv ;
所以,在你的情況下,它變成了:
yargs.options({
'foo': {
type: 'string'
},
'bar': {
type: 'string'
},
'baz': {
type: 'string'
},
})
您可以為常量提取一些選項:
const fooOption = {
'foo': {
type: 'string'
}
} as const;
const barOption = {
'bar': {
type: 'string'
}
} as const;
const bazOption = {
'baz': {
type: 'string'
}
} as const;
yargs.options({
...fooOption,
...barOption,
...bazOption
});
是的,這是可能的,但並不簡單。 您可能需要考慮按照其他人的建議以不同的方式構建代碼; 我對yargs
不熟悉,所以我不能對此發表評論。
此解決方案使用兩個一次性類型別名,您可以將它們復制並粘貼到它們的用法中以刪除它們,或者根據您可能希望在函數中輸入內容的方式對它們進行不同的構建。
// https://stackoverflow.com/a/50375286/2398020
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type YargsBuilder<A extends Argv> = (y: Argv) => A;
type _getYargBuildersTypeUnion<A extends YargsBuilder<any>[]> = {
[K in keyof A]: A[K] extends YargsBuilder<Argv<infer T>> ? T : never
}[Exclude<keyof A, keyof []>];
type _withOptionsResult<A extends YargsBuilder<any>[]> =
YargsBuilder<Argv<UnionToIntersection<_getYargBuildersTypeUnion<A>>>>
export function withOptions<A extends YargsBuilder<any>[]>(...args: A): _withOptionsResult<A>;
export function withOptions(...args: YargsBuilder<any>[]): YargsBuilder<any> {
return null as any; // TODO
}
測試用法:
// YargsBuilders
const A0 = (arg: Argv<{ x: string }>) => arg;
const A1 = (arg: Argv<{ five: 5 }>) => arg;
const A2 = (arg: Argv<{ ids: number[] }>) => arg;
const X = withOptions(A0, A1, A1, A2);
// const X: YargsBuilder<Argv<{
// x: string;
// } & {
// five: 5;
// } & {
// ids: number[];
// }>>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.