Reputation: 1534
I want to write a interface called ProptyDontHaveChildren
which doesn't have proterty called children
type ChildrenType = Array<number>
interface ProptyDontHaveChildren {
// doesn't have property called children
[index: string]: string;
}
interface WithChildren {
children: ChildrenType
}
type IntersectionType = ProptyDontHaveChildren & WithChildren;
function createElement(type: string, props: ProptyDontHaveChildren, ...children: ChildrenType ) {
const newProps:IntersectionType = { children: children, ...props }
//TODO ...
}
How to define a interface which doesn't have some property in TypeScript?
Upvotes: 3
Views: 788
Reputation: 330466
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 number
s:
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!
Upvotes: 5
Reputation: 40712
You can use the type never
to indicate that children
should not exist on ProptyDontHaveChildren
:
type ChildrenType = Array<number>
interface ProptyDontHaveChildren {
[index: string]: string;
children: never;
}
interface WithChildren {
children: ChildrenType
}
type IntersectionType = ProptyDontHaveChildren & WithChildren;
function createElement(type: string, props: ProptyDontHaveChildren, ...children: ChildrenType) {
// OK. No error
const newProps:IntersectionType = { children: children, ...props }
}
Upvotes: 1