英文:
Wrapper component that passes props to children render function
问题
-
在
Wrapper
组件中,当调用children(rest)
时,编译器指示该参数可分配给类型P
的约束,但P
可能会用不同的子类型来实例化,从而不兼容。在这里可能会实例化与此不兼容的P
的一种情况是,当你在其他地方将Wrapper
组件使用时,传递了与P
约束不匹配的属性。这可能导致编译器认为rest
参数不符合P
的约束。通过将rest
强制转换为P
,你可以解决此问题,但需要确保在所有使用Wrapper
组件的地方都符合P
的约束。 -
在
WrappedInput
组件中,编译器认为props
对象(在子级渲染函数中)具有children
和title
属性,这可能是因为编译器无法确定P
的确切类型,因此假设它可能包含这些属性。要解决这个问题,你可以为WrappedInput
组件明确指定P
的类型,以确保它不包含不必要的属性。例如,你可以这样做:
const WrappedInput = (inputProps: InputProps) => <Wrapper<InputProps>
title="Hello, World"
{...inputProps}
>{(props) => <input {...props} />}
</Wrapper>;
这样,你明确指定了 Wrapper
组件的 P
类型为 InputProps
,以确保不会出现类型错误。
英文:
I'm trying to type a wrapper component that takes a title
prop and a children
prop that's a function. All the other props passed to the wrapper should be used as an argument when rendering the children:
import React, { ReactNode, InputHTMLAttributes } from "react"
type WrapperProps<P extends object> = {
title: string
children: (props: P) => ReactNode
} & P
const Wrapper = <P extends object>({ title, children, ...rest }: WrapperProps<P>) => {
return children(rest)
}
type InputProps = { type: string, name: string }
const WrappedInput = (inputProps: InputProps) => <Wrapper
title="Hello, World"
{...inputProps}
>{(props) => <input {...props} />}
</Wrapper>
I can't understand two things:
-
In the
Wrapper
component, when callingchildren(rest)
the compiler indicates that the argument is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'object'. What couldP
be instantiated with that would be incompatible here? I can castrest as P
but I'd like to understand what the issue is. -
In the
WrappedInput
component, the compiler thinks that theprops
object (in the children render function) haschildren
andtitle
properties. How can that be when I've explicitly said that the argument thatchildren
are being called with is of typeP
andP
doesn't have those properties?
答案1
得分: 1
主要问题是,正如您在第二个问题中提到的,传递给渲染函数的props的类型是P
,但作为泛型,P
用于捕获所有props,而不管它们如何分割和使用。
所以对于您的第一个问题,如果您考虑一种情况,您希望传递给children
的props也包含一个标题字段,例如,并且该类型不一定是字符串,那么这些类型就不相关。
对于您的第二个问题,这也涉及到第一个问题,最快的方法,同时改变当前结构最少的方法,是将children参数的类型指定为Omit<P, "title"|"children">
,如您可以在这里看到的那样。
这不会完全符合您的要求,泛型P
仍然具有属性children
和 title
。您可以尝试说 P
扩展了一些不允许这些属性的东西,比如 P extends Omit<Record<string, unknown>, "title"|"children">
来满足这个条件,但我相当确定在 TypeScript 中这不起作用,您不能指定扩展某个记录并否定某些字段,参考 https://github.com/microsoft/TypeScript/issues/31153。
但是不要认为这是个大问题,如果P
具有所有属性,您只需从children渲染参数中省略它们即可。
type WrapperProps<P extends object> = {
title: string;
children: (props: Omit<P, "title"|"children">) => ReactNode;
} & P;
您可能还希望将 object
更改为类似于 Record<string, unknown>
这样的东西,因为 object
将接受诸如数组之类的东西,通常情况下,Record
更适合props。
您还可以更改您的类型,以使P
可以完全成为children的props,但然后您无法将它们扩展到主props类型。类似于这样:
type WrapperProps<P extends Record<string, unknown>> = {
title: string;
children: (props: P) => ReactNode;
childrenProps: P;
};
然后,类型就不会存在不确定性,因为P
仅用作children函数的参数,并作为WrapperProps
上的自己的字段。
希望这有所帮助。
英文:
the main issue here is, as you mentioned in second question, the props passed to the render function is of type P
but as a generic, P is being used to capture all the props, regardless of how they are split and used.
So for you first question, if you consider a situation where the props you want to be passed to the children
also contain a title field for example, and that the type was not necessarily to be a string, then the types are not related.
For your second question, this also touched upon the first question, the quickest approach whilst changing least the current structure is to specify the type of children arg as `Omit<P, "title"|"children"> as you can see here.
This won't be exactly as you specified, the generic P
will does still have properties children
and title
. You could try to say P
extends something that does not allow those such as P extends Omit<Record<string,unknown>, "title"|"children"
to try and satisfy that condition but fairly sure that does not work in typescript, you can't specify to extend some record negating certain fields, linked to https://github.com/microsoft/TypeScript/issues/31153.
But don't think that's a big deal, it's fine if P
has all the properties, and you can just omit them from children render args.
type WrapperProps<P extends object> = {
title: string
children: (props: Omit<P,"title"|"children">) => ReactNode
} & P
You probably also want to change object
to something like Record<string,unknown>
since object will accept things like arrays, Record is generally better for props.
You can also change your types so that P
can be exactly the props for children but then you cannot spread them onto that main props type. Something like this
type WrapperProps<P extends Record<string, unknown>> = {
title: string;
children: (props: P) => ReactNode;
childrenProps: P;
};
Then there is no uncertainty with the types as P
is used only as args for children function and as it's own field on WrapperProps
.
Hope it helps
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论