I have this basic layers
object, and I want to add things to each layer later.
layers = {
top: {
description: 'everything on top',
},
bottom: {
description: 'everything below',
}
}
Here is how I add things to each layer:
addThingsToLayers() {
Object.entries(this.layers).forEach(([layerName, layerObject]) => {
layerObject.element = document.createElement('div')
layerObject.style = {background: 'hotpink'}
})
}
This causes 2 expected errors
Property 'element' does not exist on type '{ description: string; }'
Property 'style' does not exist on type '{ description: string; }'
I can think of 2 ways of fixing this, but both solutions have drawbacks that I'd like to avoid.
First, I could just be more explicit in my layers
object:
layers = {
top: {
description: 'everything on top',
element: undefined,
style: undefined,
},
bottom: {
description: 'everything below',
element: undefined,
style: undefined,
}
}
Drawback: I have to write a lot of extra lines.
The other way I can think of is defining an interface for the layers
object
interface Layers {
[key: string]: {
description: string
element: HTMLDivElement
style: any
}
}
However, because of [key: string] I loose the ability to limited to the actually defined layer keys, eg
addToTopLayer() {
this.layers.
}
At this.layers.
I don't get autocomplete, and can write any key, even if it does not actually exist.
I could fix this by limiting the keys to a type, like
type LayerName = 'top' | 'bottom'
Drawback: Now I have to make edits in 2 place whenever I add a new layer, so I'm duplicating myself.
Finally my question (thanks for reading this far:):
Is there a way more elegant to achieve this, without having to write redundant or duplicate code?
You can use a Layer
interface defined with optional properties :
interface Layer {
description: string;
element?: HTMLDivElement;
// ^−−−−−−−−−−−−−−−−−−−−−−−− optional
style?: any;
// ^−−−−−−−−−−−−−−−−−−−−−−−−−− optional
}
Full example:
interface Layers {
top: Layer;
bottom: Layer;
}
interface Layer {
description: string;
element?: HTMLDivElement;
style?: any;
}
const layers: Layers = {
top: {
description: "everything on top",
},
bottom: {
description: "everything below",
},
};
I think, it is better to have map of layers
descriptions and create a new layers
object in your addThingsToLayers
function
Like this:
enum LayerNames {
TOP = "top",
BOTTOM = "bottom",
}
interface Layer {
description: string
element: HTMLDivElement
style: any; // it is better to use React.CSSProperties(for example) instead of any
}
type Layers = Record<LayerNames, Layer>;
const LAYERS_DESCRIPTIONS: Record<LayerNames, string> = {
[LayerNames.TOP]: "everything on top",
[LayerNames.BOTTOM]: "everything below",
}
addThingsToLayers(){
this.layers = Object.entries(LAYERS_DESCRIPTIONS).reduce((layers, [name, description]) => ({
...layers,
[name]: {
description,
style: {background: 'hotpink'},
element: document.createElement('div'),
}
}), {} as Layers)
}
I have had the same issue. Reading the typescript documentation, i could see that with mapped types there was a solution:
interface Layer { description: string; element?: HTMLDivElement; style?: any; }; type LayerKey = {'top':null; 'bottom': null; 'someOtherType': null}; type Layers = { -readonly [Property in keyof LayerKey]?: Layer; }; const layers:Layers = { 'top': { description: 'string', element: (document.createElement('DIV') as HTMLDivElement), style: 'any' } }; console.log(layers.someOtherType);
Said that, there are two (or more, i am not sure) things to consider:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.