I'm building a project on vanilla JS using TS.
I have a custom storage class which is stored in window object so I can interact with it wherever:
CustomStorage.ts
class CustomStorage {
private static _core: Record<string, any> = {};
private _core: Record<string, any>;
constructor() {
if (!CustomStorage._core)
CustomStorage._core = {};
this._core = CustomStorage._core;
(window as any).CustomStorage = this;
}
get items(): Record<string, any> {
return this._core;
}
setItem(prop: string, value: any): void {
this._core[prop] = value;
}
clearStorage(): void {
this._core = {};
}
}
export default CustomStorage;
I use this storage in many files throughout the project, and I want to know which fields my storage has in any external file
For example I have a separate file where I fill my storage to use this data later:
(Not full file):
export default function fillStorage() {
/**
* TableHeaders - needed for the table to print only exact columns
* Also stores into the Storage to be able to be called later
*/
const tableHeaders: string[] = ['ProdCode', 'Customer', 'ProdName', 'HostName', 'MatNum', 'ArticleNum', 'WkStNmae', 'AdpNum', 'ProcName', 'AVO', 'FPY', 'CountPass', 'CountFail', 'tLogIn', 'tLogOut', 'tLastAcc'];
Storage.setItem('tableHeaders', tableHeaders);
Storage.setItem('selectedHeaders', []);
if (Storage.items.data) {
// StaticData - stored to be a full version of initial array
Storage.setItem('staticData', Storage.items.data);
// AllHeaders - needs for reset listener to fill dropdown immediately
Storage.setItem('allHeaders', Object.keys(Storage.items.staticData[0]));
// StaticDataLength - stored, not to calculate length later
Storage.setItem('staticDataLength', Storage.items.staticData.length);
Storage.setItem('headers', Object.keys(Storage.items.data[0]));
// AllValues - as same as allHeaders. not to calculate later. Receives all present value from the static data
Storage.setItem('allValues', DropdownValues(Storage.items.staticData, Storage.items.tableHeaders));
// Stored not to keep text present as it takes lot of memory
Storage.setItem('inputTextLength', Storage.items.data.length);
}
And when I want to use this data later in a file like
index.ts:
const handleFiltersEraserClick = async (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (target?.id.substring(0, 6) === 'eraser') {
const targetId: string = target?.id.slice(7);
Storage.items.inputFields[+targetId - 1].value = '';
Storage.items.dbSelects[+targetId - 1].selectedIndex = 0;
Storage.items.dataSourceOption === 'Datenbank'
? await DBQuery()
: Storage.setItem('data', getFilters() as object[]);
if (rowsAmount) {
Storage.items.data.length === 0
? rowsAmount.innerHTML = '0'
: rowsAmount.innerHTML = Storage.items.data.length;
}
let dropdownValues: { values: string[], valueToHeaderMap: object } | null = DropdownValues(Storage.items.data, Storage.items.tableHeaders);
Storage.items.datalists.forEach((datalist: HTMLDataListElement) => {
datalist.innerHTML = '';
dropdownValues?.values.forEach((value: string) => {
const option: HTMLOptionElement = document.createElement('option');
option.className = 'datalist-option';
option.value = value;
datalist.appendChild(option);
});
});
dropdownValues = null;
}
};
I shall guess if I chose right field or not. That's why I want to have an interface
(probably) to know all the time what fields I have.
I could just create a static interface, but I have many cases when new fields are being added to the storage after fillStorage() call
Is it possible to extend initial interface when something is being added into storage? Making static interface
could lead to issues when I have such field in the interface, but it will be undefined
cause it still doesn't exist
I have initial interface
interface Storage {
staticData: object[],
allHeaders: string[],
staticDataLength: number,
headers: string[],
allValues: object[],
inputTextLength: number
}
after something happened and I added f.e.
Storage.setItem('numbers', [1, 2, 3])
I need then this number
to appear in interface Storage
so I will have
interface Storage {
staticData: object[],
allHeaders: string[],
staticDataLength: number,
headers: string[],
allValues: object[],
inputTextLength: number,
numbers: number[]
}
And if I delete field from Storage
to delete corresponding field from the interface
Hope I've been clear in my question. Any recommendations and advices would be highly appreciated
I think you are looking in the wrong places, and I recommend you look at the compiled JavaScript output from TypeScript. Interfaces do not emit any code at all, they are only there to describe objects within TypeScript. That's the "type" in TypeScript!
A few tips about types:
object
for a type, that is far too generic. Better describe your objects instead.any
type - since it can be anything why bother using TypeScript at all? It's best to describe your objects instead.I
so it's easier to tell what is an interface and what is not?
suffix on the property nameHere's a demo rewrite of your type with some made up interfaces to use instead of object
. Note the optional properties
interface IData {
foo: string;
bar?: number;
}
interface IValue {
baz: string;
val?: number;
}
interface IStorage {
staticData: IData[],
allHeaders: string[],
staticDataLength: number,
headers: string[],
allValues: IValue[],
inputTextLength: number
}
interface IStorageWithNumbers extends IStorage {
numbers: number[]
}
const myStorage: IStorageWithNumbers = {
staticData: [{foo: 'a'}, {foo: 'b', bar: 2}],
allHeaders: [''],
staticDataLength: 0,
headers: [''],
allValues: [{baz: 'c', val: 3}, {baz: 'd'}],
inputTextLength: 0,
numbers: [1, 2, 3]
};
Please check this TS Playground link and see the JS output from it by selecting ".JS" on the right side