在作为道具传递之前,TypeScript无法识别在forEach循环范围内的对象上设置的值



我试图将对象道具传递到具有道具类型定义的功能组件中。我首先在forEach循环的范围内为每个必需的道具类型属性设置值,TypeScript似乎无法识别:

const preparePayload = (initPayload) => {
const initSubmissionState = {'e':'e'}
const attributes = ['a', 'b', 'c','d']
attributes.forEach (a => {
if (initPayload.suggested_attributes && initPayload.suggested_attributes[a]) {
initSubmissionState[a] = initPayload.suggested_attributes[a]
} else if (initPayload[a]){
initSubmissionState[a] = initPayload[a]
} else{
initSubmissionState[a] = ''
}
})
return initSubmissionState
}
interface Component2Props {
a: string,
b: string,
c: string,
d: string
}
const Component2 = (props:Component2Props) => {
return (
<>
<div>{props.a}</div>
<div>{props.b}</div>
<div>{props.c}</div>
<div>{props.d}</div>
</>
)
}
const initPayload = {
a: 'a',
c: 'c',
suggested_attributes: {
a:'A'
}
}
const Component = (props) => {
const payload = preparePayload(props)
return (
<Component2 {...payload} />
)
}

我在将有效负载传递到Component2时遇到一个类型错误:Type '{ e: string; }' is missing the following properties from type 'Component2Props': a, b, c, d

是否必须在TypeScript的helper函数中显式设置每个属性才能将其作为道具?

您的问题有两个组成部分。首先,您尝试将未知密钥(abc(分配给对象initSubmissionState,该对象只知道属性d。简称:

const initSubmissionState = { d:'d' }
initSubmissionState.a = 'a'

TypeScript沙盒

解决方案:对变量initSubmissionState:进行显式键入

const initSubmissionState: Partial<Record<'a' | 'b' | 'c' | 'd', string>> = { d:'d' }
initSubmissionState.a = 'a'

TypeScript沙盒

第二个组件是typescript不能正确地干扰attributes数组的类型。默认情况下,它为数组选择最小的公共超类型,在本例中为string,这意味着以下操作不起作用:

const initSubmissionState: Partial<Record<'a' | 'b' | 'c' | 'd', string>> = { d:'d' }
const attributes = ['a', 'b', 'c', 'd']
attributes.forEach(attr => {
// TypeScript interferes attr to be of type string, not 'a' | 'b' | 'c' | 'd'
initSubmissionState[attr] = '' // error because initSubmissionState only has keys 'a', 'b', 'c' and 'd' but TypeScript thinks attr is any string
})

最后一步是让TypeScript正确地干扰attributes数组的类型:

const asStringLiteralArray = <T extends string>(...values: T[]) => values
const initSubmissionState: Partial<Record<'a' | 'b' | 'c' | 'd', string>> = { d:'d' }
const attributes = asStringLiteralArray('a', 'b', 'c', 'd')
attributes.forEach(attr => {
initSubmissionState[attr] = ''
})

TypeScript沙盒

现在让我们把它们放在一起:

const preparePayload = (initPayload: Record<string, string>) => {
const initSubmissionState: Partial<Component2Props> = {'d':'d'}
attributes.forEach(a => {
if (a in initPayload) {
initSubmissionState[a] = initPayload[a]
} else {
initSubmissionState[a] = ''
}
})
return initSubmissionState as Component2Props
}
const asStringLiteralArray = <T extends string>(...values: T[]) => values
const attributes = asStringLiteralArray('a', 'b', 'c','d')
type Component2Props = Record<typeof attributes[0], string>;
const Component2 = (props:Component2Props) => {
return (
<>
<div>{props.a}</div>
<div>{props.b}</div>
<div>{props.c}</div>
</>
)
}
const initPayload = {
a: 'a',
c: 'c'
}
const Component = (props) => {
const payload = preparePayload(props)
return (
<Component2 {...payload} />
)
}

编辑:下次请尝试构建一个最小的可执行示例,这将使其他人能够在没有太多额外工作的情况下尝试他们的修复。

最新更新