使用下面的代码,我试图根据fn
的值来推断args
的参数类型。
我需要将good
推论为布尔值,将num
推论为数字,将bad
推论为错误。
现在它们的类型均为string | number | boolean
,并且没有错误。
type WithArgs<
Args extends {
[key: string]: { value: [string, string | number | boolean] };
} = {
[key: string]: { value: [string, string | number | boolean] };
},
Arg extends keyof Args = keyof Args
> = {
args: Args;
fn: (params: { [key in Arg]: Args[Arg]["value"][1] }) => void;
};
const configs: WithArgs[] = [{
args: { good: { value: ["bool", true] } },
fn: ({ good, bad }) => {}
}, {
args: { num: { value: ["number", 5] } },
fn: ({ num }) => {}
}];
答案 0 :(得分:0)
Typescript只能推断函数的类型。由于您尝试键入非函数,因此泛型类型必须包含一个参数。
如果我为该类型提供第一个参数,则good
只能为true,并且我们会收到错误消息,指出“ TS2339:类型'{不存在属性'bad','good:true ;}'。”
const configs: WithArgs<{ good: { value: ['bool', true] } }>[] = [{
args: { good: { value: ["bool", true] } },
fn: ({ good, bad }) => {}
}];
当然,我不确定您为什么要这么做。我很想知道?。
编辑(playground link):
我接受了自己的建议。知道Typescript 可以从函数中推断类型,那么我们要做的就是创建一个函数。
首先,我为Args
添加了一个定义,并更新了WithArgs
类型:
type Args<T> = {
[key: string]: { value: [string, T] };
};
type WithArgs<
A extends Args<unknown> = {
[key: string]: { value: [string, string | number | boolean] };
},
Arg extends keyof A = keyof A
> = {
args: A;
fn: (params: { [key in Arg]: A[Arg]["value"][1] }) => void;
};
然后,我定义一个函数来推断单个配置的类型:
const withArgs = <C, A extends Args<C>, F extends keyof A>(config: WithArgs<A, F>) => config;
现在,我们根据需要收到bad
的错误:
const invalid = withArgs({
args: { good: { value: ["bool", true] } },
fn: ({ good, bad }) => {} // Property 'bad' does not exist on type '{ good: boolean; }'
});
当然,您希望能够定义这些配置的数组。因此,我们可以定义一个可以正确键入数组的函数:
type GetWithArgs<T> = T extends WithArgs<infer A, infer F> ? WithArgs<A, F> : never;
const withArgsArr = <T extends WithArgs<Args<unknown>, string | number>[]>(...configs: T) => configs.map(withArgs) as { [i in keyof T]: GetWithArgs<T[i]> };
现在,我还没有弄清楚这一点,但是当您以这种方式声明整个配置时,似乎并没有引发错误。类型错误仅在您随后引用特定配置时发生。我希望有人可以帮助您澄清这种行为(编辑:I've now asked the question here)。
const shouldAlsoBeInvalid = withArgsArr(
{
args: { good: { value: ["bool", true] } },
fn: ({ good, bad }) => {}
}, {
args: { num: { value: ["number", 5] } },
fn: ({ num }) => {}
}
); // no errors yet :(
const requiresBool = shouldAlsoBeInvalid[0].fn({ good: 'string' }); // TS2322: Type 'string' is not assignable to type 'boolean'.
const badDoesNotExist = shouldAlsoBeInvalid[0].fn({ bad: 'string' }); // TS2345: Argument of type '{ bad: string; }' is not assignable to parameter of type '{ good: boolean; }'.
const requiresNumberParameter = shouldAlsoBeInvalid[1].fn({ num: '1' }); // TS2322: Type 'string' is not assignable to type 'number'.