i have a Typescript class responsible for storing and retrieving any object, its type as well as the date when the object was stored in LocalStorage (code below)
Problem is the "storageDate" property. Though during compile time, TS mentions the type to be "Date" but at runtime its a string. So i can not use for eg toDateString() function on storageDate. Any idea why this is happening and suggestions on how to write it?
//Class
export interface ILocalStorageReturnValue<T>{
storageDate:Date;
storedObj:T
}
export class LocalStorage<T> implements ILocalStorage<T>
{
constructor(){
if( !window.localStorage)
throw new Error(`[${constants.FrameWorkName}.LocalStorage]:${strings.ErrBrowserNoLclStrg}`)
}
public setItem(key: string, val: T): void{
if(!key || !val )
throw new Error(`[${constants.FrameWorkName}.LocalStorage.setItem]:${strings.ErrMissingKeyValProp}`)
const newObj:ILocalStorageReturnValue<T> = {
storedObj: val,
storageDate: new Date()
}
window.localStorage.setItem(key ,JSON.stringify(newObj))
}
public getItem(key: string): ILocalStorageReturnValue<T> | null{
return JSON.parse( window.localStorage.getItem(key)) ? <ILocalStorageReturnValue<T>>JSON.parse( window.localStorage.getItem(key)):null
}
}
// access
const ls = new LocalStorage<IImage>().getItem(constants.localStorageKey)
ls.storageDate().toDateString() --> throws an error saying toDateString is not a function .
CodePudding user response:
The problem is that your code doesn't ensure the shape of the object is correct when you retrieve it from local storage. JSON doesn't have any concept of dates, but you're returning the result of JSON.parse
(without any reviver function) and telling TypeScript that that is a ILocalStorageReturnValue<T>
. But it isn't one, because ILocalStorageReturnValue
's storageDate
is a Date
, and JSON.parse
(without a reviver function) will never create a Date
. You have to do that in your code. The type assertion¹ is incorrect, but TypeScript trusts what you tell it.
Since you're relying on the default handling of dates when you store the item, the storageDate
string in local storage will be compatible with new Date
, so you could do this:
public getItem(key: string): ILocalStorageReturnValue<T> | null {
const str = window.localStorage.getItem(key);
if (!str) {
return null;
}
const result = JSON.parse(str);
result.storageDate = new Date(result.storageDate);
return result;
}
¹ Note that TypeScript doesn't have casting, it has type assertions. A type assertion is purely a compile-time thing. It's you telling TypeScript the type of something that it may not be able to figure out the type of. Type assertions have no effect whatsoever at runtime.