There are quite a few problems here. Be warned:
You could make children an optional property of type never or undefined in ProptyDontHaveChildren (it's important that the property be optional):
type ChildrenType = Array<number>
interface ProptyDontHaveChildren {
[index: string]: string;
children?: never; // note the ?
}
That guarantees that ProptyDontHaveChildren can only be created from something with a missing or undefined children property.
But now the intersection IntersectionType will not be what you want: it also cannot have children because the intersection demands that children be both of type undefined and of type ChildrenType, which can't happen:
let oops: IntersectionType = { children: [1, 2, 3] } // error
So the best thing to do would be to define ProptyDontHaveChildren as the intersection of a base Propty type and a WithoutChildren type, so that you can then define ProptyHaveChildren (what you wanted IntersectionType to be) as the intersection of Propty and WithChildren. Like this:
interface Propty {
[index: string]: string;
}
interface WithoutChildren {
children?: never
}
interface WithChildren {
children: ChildrenType
}
type ProptyDontHaveChildren = Propty & WithoutChildren
type ProptyHaveChildren = Propty & WithChildren
But there's still a problem. The ProptyHaveChildren type still can't have a property of type children, because the index signature requires that every property, including children, be of type string. So children must be both a string and an array of number, which can't happen:
const proptyHaveChildren: ProptyHaveChildren = {
a: "a",
children: [1, 2, 3]
}; // error!
function createElement(type: string, props: ProptyDontHaveChildren, ...children: ChildrenType) {
// error!
const newProps:ProptyHaveChildren = { children: children, ...props }
}
From here I'm not sure how you want to proceed. TypeScript lacks subtraction types, which is what you'd need to say that the index signature should refer to every string key except "children". You could open up the Propty type so that every property is either a string or an array of numbers:
interface Propty {
[index: string]: string | ChildrenType;
}
function createElement(type: string, props: ProptyDontHaveChildren, ...children: ChildrenType) {
// no error
const newProps:ProptyHaveChildren = { children: children, ...props }
}
That works, but now every property will accept an array of numbers:
const proptyHaveChildren: ProptyHaveChildren = {
a: [1, 2, 3],
children: [1, 2, 3]
}; // no error!
That's probably not what you want.
At this point I notice I'm fighting with TypeScript to force it to understand your interface. Maybe the best thing to do is to change your representation of Propty so that it contains two properties: a props property to hold all those string properties, and children:
type ChildrenType = Array<number>
interface Propty {
props: { [index: string]: string }
}
interface WithoutChildren {
children?: never
}
interface WithChildren {
children: ChildrenType
}
type ProptyDontHaveChildren = Propty & WithoutChildren
type ProptyHaveChildren = Propty & WithChildren
const proptyHaveChildren: ProptyHaveChildren = { props: { a: "a" }, children: [1, 2, 3] }; // works
function createElement(type: string, props: ProptyDontHaveChildren, ...children: ChildrenType) {
const newProps: ProptyHaveChildren = { children: children, props: props.props } // works
}
Now TypeScript understands, and everything works... at the expense of pulling your type apart into multiple sub-properties. You might prefer your original structure. It's up to you whether you prefer it enough to deal with the issues above.
Hope that helps. Good luck!
delete props['children']before creating newProps or even simply:newProps = { ... props}; newProps.children = children?