Reactjs TypeScript:如何将类型信息从父级传递给子级?
给定以下Reactjs TypeScript:如何将类型信息从父级传递给子级?,reactjs,typescript,Reactjs,Typescript,给定以下表单及其(非受控)输入子项: <Form initialValues={{ firstName: "", lastName: "", age: "" }}> <Input label="First name" name="firstName" /> <Input label="Last name" name="lastNa
表单
及其(非受控)输入
子项:
<Form initialValues={{ firstName: "", lastName: "", age: "" }}>
<Input label="First name" name="firstName" />
<Input label="Last name" name="lastName" />
<Input label="Age" name="age" />
</Form>
我希望输入
的名称
道具的类型为“firstName”|“lastName”|“age”
。此类型应派生自表单
的初始值
实现这一目标最干净的方法是什么
注意:在一般情况下,表单输入组件应该是可树摇动的。不确定输入是如何定义的,但我会使用formname类型为输入组件创建一个formname类型和一个接口
键入FormName=“firstName”|“lastName”|“age”;
接口输入道具{
姓名:FormName
}
//或
接口输入道具{
姓名:“名字”|“姓氏”|“年龄”
}
常量输入:FC=(props):JSX.Element=>{
};
您可以使用keyof
type initialValues = {
firstName: string;
lastName: string;
age: string
}
type inputName = keyof initialValues;
let age: inputName = "age"; //valid
let middleName: inputName = "middleName"; //invalid
编辑:我觉得我遗漏了什么,但这里有一个代码沙盒,展示了我将如何实现它:
这样的事情不可能吗?
表单
和输入
是否由第三方模块提供?您只需在此限制您的输入
组件的属性名称
,可以这样实现:
导出类型InputName=“firstName”|“lastName”|“age”
导出接口InputProps{
名称:InputName
}
常量输入:React.FC=(道具)=>{
常量{name}=props
// ...
}
然而,这意味着应用程序中的每个输入都应该有一个这样的名称,我很确定这不是你想要的
为确保特定的表单中的每个输入
组件具有以下名称之一,您需要定义一个新的自定义组件,该组件将扩展默认行为:
specific form.tsx
从“组件/表单”导入表单
从“组件/输入”导入输入,{InputProps}
导出类型SpecificInputName=“firstName”|“lastName”|“age”
导出接口SpecificInputProps扩展InputProps{
名称:InputName
}
导出类型SpecificFormComponent=React.FC&{Input:React.FC}
常量SpecificForm:SpecificFormComponent=(道具)=>{
返回{props.children}
}
const SpecificInput:React.FC=(道具)=>{
返回
}
SpecificForm.Input=SpecificInput
导出默认特定表单
然后你可以像这样消费它:
从“./components/SpecificForm”导入表单
此信息无法自动推断。您必须以某种方式手动提供该类型。临时解决方案如下所示:
function App() {
const initialValues = {
firstName: 'string',
lastName: 'string',
age: 'string',
}
const MyInput: React.FC<{ label: string, name: keyof typeof initialValues }> = Input
return <Form initialValues={initialValues}>
<MyInput label="First name" name="firstNameWRONG" /> // error
<MyInput label="Last name" name="lastName" />
<MyInput label="Age" name="age" />
</Form>
}
首先,如果要引发任何类型错误,则应通过parentElement
行而不是childElement
行引发。违反的是父组件的协议,该协议规定“子组件的名称
应为keyof initValue
”。这种验证是通过React.createElement
函数针对其参数进行的
其次,从类型系统的角度来看,如果我们能够从parentProps
推断childProps
的类型,那么解析过程应该如下所示:
1. let `Parent` be generic type of form
Component<T, E<_>> = (props: { initValues: T, children: E<keyof T> }) => Element<any>
2. let `Child` be generic type of form
Component<K> = (props: { name: K }) => Element<K>
3. from `childElement = React.createElement(Child, childProps))`
we know `childElement` is type `Element<string>`
4. from `parentElement = React.createElement(Parent, parentProps, childElement)`
we know about `T` and `E = Element` and `_ = keyof T`
5. now we need to allow prioritze `E<_>` rule over `Element<string>`, thus override `childElement` from `Element<string>` to `Element<keyof T>`
但我们的意思是
fill<T>(arg0: T, arg1: Element<T>)
fill(arg0:T,arg1:Element)
顺便说一句,从来没有一个类型系统支持这种行为,更不用说TS一开始甚至不支持更高级的类型
--
更新
我认为值得一提的是,理论上有可能从表单
关于名称
属性的输入
不符合协议的情况下引发类型错误。但是,这不能用JSX实现,只能通过React.createElement
实现
这是因为TS将所有JSX创建的元素分配给特殊的内置接口JSX.Element
。react对其进行了扩展,以扩展注释中提供的react.ReactElement。您的意思是要使用循环来创建三个输入,而不是对其进行硬编码?为什么不简单地使用受控输入?将它们的值存储在其父级的状态中,使用“值”字段将这些值传递给它们,并在使用onChange回调的输入时更新父级的状态。在这里,您可以找到一些解决方法缺少一个关键点。输入
如何在传递给父表单
的初始值
上执行keyof
?这种方法的缺点是表单组件不会发生树抖动。假设您有10个不同的表单输入组件,如input
,Select
,RadioGroup
,等等。如果用户创建了一个包含2个input
字段的简单表单,那么它们应该只包括其捆绑包中的表单
和input
组件。根据您的方法,所有10个组件都将包括在内,因为它们都是在表单上定义的。您是对的,这就是为什么这个组件需要是一个特定的组件(如LoginForm
),您一定要使用它(如果您不使用它,就没有理由创建它).该问题提到了驱动该类型的表单
。你的回答没有多大意义,因为我没有看到表单。
fill<T>(arg0: T, arg1: Element<T>)