xiang
xiang

Reputation: 1534

How to define a interface which doesn't have some property in TypeScript?

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

Answers (2)

jcalz
jcalz

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 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!

Upvotes: 5

Saravana
Saravana

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

Related Questions