我想创建一个函数(称为outerFn
),它被赋予一个函数(称为innerFn
)作为参数。innerFn
函数被赋予一个对象类型参数,该对象将被要求具有user
参数。然后outerFn
应该返回一个函数(称为returnFn
),该函数应该具有与innerFn
类似的类型,但其参数对象不应该需要user
参数(但仍然需要所有其他参数)。
我希望这样,我的outerFn
可以返回一个returnFn
,它实际上调用了innerFn
,但自动注入了user
(即而无需发送user
)
例如
type User = { user: { id: number, name: string } };
type Place = { Place: string };
type UserAndPlace = User & Place;
type InnerFn1Resolve = { something: string };
type InnerFn1 = (args: UserAndPlace) => Promise<InnerFn1Resolve>;
const innerFn1: InnerFn1 = async (args) => Promise.resolve({ something: 'yes' });
type ReturnFn1 = (args: Place) => Promise<InnerFn1Resolve>;
const returnFn1: ReturnFn1 = outerFn(innerFn1);
type Job = { job: number };
type UserAndJob = User & Job;
type InnerFn2Resolve = { else: number };
type InnerFn2 = (args: UserAndJob) => Promise<InnerFn2Resolve>;
const innerFn2: InnerFn2 = async (args) => Promise.resolve({ else: 1 });
type ReturnFn2 = (args: Job) => Promise<InnerFn2Resolve>;
const returnFn2: ReturnFn2 = outerFn(innerFn2);
我在outerFn
上尝试了以下操作。上面的输入是正确的...但是outerFn
返回抱怨。
type ArgsWithUser = Record<string, unknown> & User;
type InnerFn<Args extends ArgsWithUser, Response> = (args: Args) => Promise<Response>
type ReturnFn<Args extends ArgsWithUser, Response> = (args: Omit<Args, 'user'>) => Promise<Response>
const outerFn = <Args extends ArgsWithUser, Response>(innerFn: InnerFn<Args, Response>): ReturnFn<Args, Response> => {
const user: User['user'] = {
id: 1,
name: 'Something',
};
return async (argsWithoutUser) => innerFn({ ...argsWithoutUser, user });
};
对于innerFn
的参数,我得到以下错误(即{ ...argsWithoutUser, user }
)
类型为'Omit<Args,“user”> & { user:“User; }”不能分配给“Args”类型的参数。'忽略<Args,“user”> & { user:User; }'可分配给类型为' Args '的约束,但'Args'可使用约束' ArgsWithUser '的不同子类型示例化。ts(2345)
我只是...真的不明白这个错误。我知道它试图说返回值可能不完全匹配Args
......但我也不明白为什么会这样,感觉我一定是以某种方式混淆了类型。
我可以将对象强制转换为Args
,但如果没有必要,我希望避免强制转换(我认为不应该这样做)。
这是一个TypeScript Playground重建,您可以在其中看到错误。
我特别想弄清楚的是 * 为什么 * 打字不正确。我已经使用TypeScript 3年多了,我认为我对为什么打字是错误的有一个很好的理解......但在这种情况下,我似乎无法理解为什么我有错。
任何帮助将不胜感激。
1条答案
按热度按时间yhqotfr81#
你得到的错误在技术上是正确的。编译器不能确定
innerFn
的参数类型是否真的接受User
作为其user
属性。下面是编译器担心的情况的一个例子:这里
innerFn
要求其参数的类型为{ user: { id: number, name: "a" | "b" } }
。这是严格窄于User
。如果尝试使用User
类型的参数调用innerFn()
,则很有可能会出现运行时错误。如果arg.user.name
既不是"a"
也不是"b"
,那么你将解引用undefined
并得到一个TypeError
。但是你的
outerFn()
的输入很乐意接受innerFn
:类型
Args
被推断为{user: {id: number; name: "a" | "b"}}
,Omit<Args, "user"> & { user: { id: number; name: string; }; }
与User
相同。但这不是innerFn
所需要的。就是问题是因此,如果你运行
returnFn()
,你会得到运行时错误,没有编译器警告:这就是问题所在这显然是不可能的,所以你可能只想Assert(你所谓的“转换”)并继续前进。
相反,如果你想重构以避免这个问题,你可以通过 * 添加 *
User
到一个类型而不是 * 省略 * 它来避免这个问题:这编译起来没有错误,并且仍然适用于您所提供的示例。这可能对你的用例有用,也可能不起作用,但至少编译器是满意的。
Playground链接到代码