Typescript React:基于另一个道具类型的有条件可选道具
[英] Typescript React: Conditionally optional props based on the type of another prop
)}</获取><Fetch fetcher={getCurrentUser} params={[]}>//<-- 冗余,因为 getCurrentUser 没有任何参数{({ 数据:用户 }) =>(<div>{user.name} {user.email}
)}</获取>
)
我想知道是否有一种方法可以说,如果 fetcher 函数不接受参数,则 params
属性不应该存在甚至是可选的?
我尝试如下修改道具,如果 fetcher
有 > 0 个参数,则只添加 params
字段
类型道具= {取物者:F儿童:(道具:ChildrenProps)=>React.ReactNode&(Arguments<F> extends []//没有参数?{}//无参数: {//和之前一样参数:参数<F>})
这实际上有效,但是现在在渲染道具中 data
字段不再正确输入.
{({ 数据:用户 }) =>(//数据:任何,但用户:从不...?<div>{user.name} {user.email}//名称/电子邮件在类型 never 上不存在
)}</获取>
我不知道为什么会这样.
<小时>
更新:
看来修改后的Props
实际上是基于以下示例工作的
type PropsWithGetUser = Propstype PropsWithGetCurrentUser = Propsconst test1: PropsWithGetCurrentUser["children"] = input =>输入.数据.emailconst test2: PropsWithGetUser["children"] = input =>输入.数据.email
工作没有错误.
PropsWithGetUser
和 PropsWithGetCurrentUser
的结果类型也被推断为以下似乎是正确的.
type PropsWithGetCurrentUser = {取物器:() =>承诺<用户>儿童:(道具:ChildrenProps<() => Promise>) =>React.ReactNode}输入 PropsWithGetUser = {取物器:({ID,}:{身份证号码},) =>承诺<用户>孩子们: (道具:ChildrenProps<({ID,}:{身份证号码},) =>承诺<用户>>,) =>React.ReactNode&{参数: [{身份证号码}]}
这个问题可能与我在 React 中使用它的方式有关吗?
解决方案
这在 TypeScript 3.0 中是可能的.
为了回答这个问题,我使用了 ArgumentTypes
from
此答案:如何从 Typescript 中的函数获取参数类型.
方法如下:
TypeScript 操场上的演示
type ArgumentTypes=F extends (...args: infer A) =>任何 ?答:从不;type Props<F extends Function,参数 = ArgumentTypes<F>>=参数扩展{长度:0}?{取物器:F;} : {取物器:F;参数:参数};function runTest(props: Props) { }函数 test0() { }功能测试1(一:数字){}运行测试({取物器:test0//, params: []//错误});运行测试({取物器:test1//注释掉,//或者用无效的参数替换它//并查看错误, 参数: [1]});
I'm trying to type a fetcher component API that I'm working on. The idea is quite simple, you give it a fetcher
(promise returning function) and a params
array (representing the positional arguments) as props, and it will provide the results to a render prop.
type FunctionType<A extends any[] = any[], R = any> = (...args: A) => R
type Arguments<T> = T extends FunctionType<infer R, any> ? R : never
type PromiseReturnType<T> = T extends (...args: any[]) => Promise<infer R>
? R
: never
type ChildrenProps<F> = {
data: PromiseReturnType<F>
}
type Props<F> = {
fetcher: F
params: Arguments<F>
children: (props: ChildrenProps<F>) => React.ReactNode
}
class Fetch<F> extends React.Component<Props<F>> {
render() {
return null
}
}
const usage = (
<div>
<Fetch fetcher={getUser} params={[{ id: 5 }]}>
{({ data: user }) => (
<div>
{user.name} {user.email}
</div>
)}
</Fetch>
<Fetch fetcher={getCurrentUser} params={[]}> // <-- Redundant since getCurrentUser doesn't have any params
{({ data: user }) => (
<div>
{user.name} {user.email}
</div>
)}
</Fetch>
</div>
)
I was wondering if there was a way to say that if the fetcher function does not take arguments, the params
prop should not be present or even optional?
I tried to modify the props as follows, only adding the params
field if fetcher
has > 0 arguments
type Props<F> = {
fetcher: F
children: (props: ChildrenProps<F>) => React.ReactNode
} & (Arguments<F> extends [] // no arguments
? {} // no params
: { // same as before
params: Arguments<F>
})
Which actually works, however now in the render prop the data
field is no longer typed correctly.
<Fetch fetcher={getUser} params={[{ id: 5 }]}>
{({ data: user }) => ( // data: any, yet user: never...?
<div>
{user.name} {user.email} // name / email does not exist on type never
</div>
)}
</Fetch>
I'm not sure why this is the case.
UPDATE:
It seems that the modified Props
is actually working from based on the following examples
type PropsWithGetUser = Props<typeof getUser>
type PropsWithGetCurrentUser = Props<typeof getCurrentUser>
const test1: PropsWithGetCurrentUser["children"] = input => input.data.email
const test2: PropsWithGetUser["children"] = input => input.data.email
that work without errors.
The resulting types of PropsWithGetUser
and PropsWithGetCurrentUser
are also inferred as the following which seems correct.
type PropsWithGetCurrentUser = {
fetcher: () => Promise<User>
children: (props: ChildrenProps<() => Promise<User>>) => React.ReactNode
}
type PropsWithGetUser = {
fetcher: (
{
id,
}: {
id: number
},
) => Promise<User>
children: (
props: ChildrenProps<
(
{
id,
}: {
id: number
},
) => Promise<User>
>,
) => React.ReactNode
} & {
params: [
{
id: number
}
]
}
Could the issue have something to do with the way I am using this with React?
解决方案
This is possible in TypeScript 3.0.
To answer the question, I used ArgumentTypes<F>
from
this answer to: How to get argument types from function in Typescript.
Here's how:
Demo on TypeScript playground
type ArgumentTypes<F extends Function> =
F extends (...args: infer A) => any ? A : never;
type Props<F extends Function,
Params = ArgumentTypes<F>> =
Params extends { length: 0 } ?
{
fetcher: F;
} : {
fetcher: F;
params: Params
};
function runTest<F extends Function>(props: Props<F>) { }
function test0() { }
function test1(one: number) { }
runTest({
fetcher: test0
// , params: [] // Error
});
runTest({
fetcher: test1
// Comment it,
// or replace it with invalid args
// and see error
, params: [1]
});
这篇关于Typescript React:基于另一个道具类型的有条件可选道具的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!