I have an object that represents a tree of locations. Each location may have sublocations(or not) and has a numeric id
{
id: 0,
USA: {
id: 1,
ARIZONA: {
id: 2,
PHOENIX: {
id: 3
},
TUSCANY: {
id: 4
}
},
ALABAMA: {
id: 4,
}
},
CANADA: {
id: 6,
ONTARIO: {
id: 7,
TORONTO: {
id: 8
},
OTTAWA: {
id: 9
}
},
QUEBEC: {
id: 10,
MONTREAL: {
id: 11
}
},
}
UK: {
id: 12,
LONDON: {
id: 13
}
}
}
I am trying to represent this as an interface
interface Location {
id: number;
[key in string]?: Location
}
but it doesn't let me saying:
A mapped type may not declare properties or methods.ts(7061) I also tried
interface Location {
id: number;
[key:string]: Location
}
but it says
Property id of type number is not assignable to string index type Location
CodePudding user response:
You can use type
instead of interface
for this. We'll use it because then we can use intersections and unions. This we will use like the following:
type Foo = { id: number } | { id: number } & { [key: string]: Foo };
Foo
can simply be an object with an id, or it can be an object with an id, plus a string index signature with more Foo objects.
If you want access to result in Foo | undefined
, you can either use the noUncheckedIndexAccess
compiler option (although rather strict), or just replace Foo
in the index signature with something like Foo | undefined
.
CodePudding user response:
type Idable = { id: number }
type IndexableLocation = { [key: string]: SomeLocation }
type SomeLocation = Idable | (Idable & IndexableLocation)
Illustration
const locations: SomeLocation = {
id: 0,
USA: {
id: 1,
ARIZONA: {
id: 2,
PHOENIX: {
id: 3
},
TUSCANY: {
id: 4
}
},
ALABAMA: {
id: 4,
}
},
CANADA: {
id: 6,
ONTARIO: {
id: 7,
TORONTO: {
id: 8
},
OTTAWA: {
id: 9
}
},
QUEBEC: {
id: 10,
MONTREAL: {
id: 11
}
},
},
UK: {
id: 12,
LONDON: {
id: 13
}
}
};
console.log((locations as IndexableLocation)["USA"]); // Type Asserion is important ere to satisfy the compiler
console.log(locations.id);