在这里,我想创建一个可以定义其依赖关系的函数,这些依赖关系在示例化时提供给函数。我无法让TS在这里给予正确的类型检查。我得到的错误是:
(parameter) d1: string | number
Argument of type 'string | number' is not assignable to parameter of type 'number'.
Type 'string' is not assignable to type 'number'
如何让TS按顺序理解args的类型,而不是将它们作为一个union集中在一起?
Playground
export type Injectable<T = any> = {
dependencies: Injectable[];
get: () => Promise<T | null>;
};
export type AsyncFactoryFn<T, Deps extends any[] = any[]> = (...args: {[K in keyof Deps]: Deps[K] extends Injectable<infer U> ? U : never}) => Promise<T>;
export function injectable<T, Deps extends Injectable<any>[]>(
asyncFactoryFn: AsyncFactoryFn<T, Deps>,
dependencies: Deps = [] as unknown as Deps,
): Injectable<T> {
return {} as Injectable<T>;
}
// EXAMPLE
const dep1 = {
dependencies: [],
get: () => Promise.resolve(1),
}
const dep2 = {
dependencies: [],
get: () => Promise.resolve('a'),
}
const injectableObject = injectable((d1, d2) => Promise.resolve(someService(d1, d2)), [dep1, dep2]);
function someService(d1: number, d2: string) {
console.log(d1, d2);
}
1条答案
按热度按时间zz2j4svz1#
你的问题是,当TypeScript遇到像
[dep1, dep2]
这样的数组文字时,默认行为是将其类型推断为像Array<typeof dep1 | typeof dep2>
这样的普通数组类型,而不知道长度或顺序。这通常是人们想要的,因为数组是可变的,你可以改变它们的内容。但现在这并不可取;你更希望编译器推断一个元组类型,比如[typeof dep1, typeof dep2]
。有不同的方法来实现这一点;您可以使用
const
Assert,如[dep1, dep2] as const
,但这需要injectable()
的调用者做一些事情。另一种方法是使
dependencies
参数具有像readonly [...Deps]
这样的可变元组类型,而不仅仅是Deps
。这对 type 没有太大影响,但它提供了一个有助于推断的元组 context。正如microsoft/TypeScript#39094中提到的,pull请求引入了可变元组类型:当数组文字的上下文类型是元组类型时,为数组文字推断元组类型。类型
[...T]
,其中T
是类似数组的类型参数,可以方便地用于指示对元组类型推断的偏好。所以我们可以这样做:
这里我用
readonly [...Deps]
替换了Deps
(注意,readonly
元组类型实际上比非readonly
元组类型更容易接受;readonly X[]
可分配给X[]
,但反之亦然)。现在你得到了你想要的行为:
Playground代码链接