I have the following code, which is very repetitious:
const flags = {
get logged() {
return localStorage.getItem("logged") === "true";
},
set logged(val: boolean) {
if (val) {
localStorage.setItem("logged", "true");
} else {
localStorage.removeItem("logged");
}
},
get notificationsMuted() {
return localStorage.getItem("notifications-muted") === "true";
},
set notificationsMuted(val: boolean) {
if (val) {
localStorage.setItem("notifications-muted", "true");
} else {
localStorage.removeItem("notifications-muted");
}
}
}
As you can see, the get and set for each flag type is identical, save for the property names. I would like to do something like this instead:
function getter(prop: string) {
return localStorage.getItem(prop) === "true";
}
function setter(prop: string, val: boolean) {
if (val) {
localStorage.setItem(prop, "true");
} else {
localStorage.removeItem(prop);
}
}
const flags = {
get logged: getter("logged")
set logged: setter("logged")
get notificationsMuted: getter("notifications-muted")
set notificationsMuted: setter("notifications-muted")
}
But I'm not sure if Javascript / Typescript has support for this sort of thing. Is such a thing possible, and if so, how? If not, is there any other way I can cut down on the repetition here?
You can use a proxy with get and set traps, use TS types to allow only props you wish to handle (TS playground)):
interface Flags {
logged: boolean,
'notifications-muted': boolean;
}
type Prop = keyof Flags;
const handlers = {
get(_: Flags, prop: Prop) {
return localStorage.getItem(prop) === "true";
},
set(_: Flags, prop: Prop, val: any) {
if (val) {
localStorage.setItem(prop, "true");
} else {
localStorage.removeItem(prop);
}
return true;
}
};
const flags = new Proxy<Flags>({} as Flags, handlers);
All you really need is to use Object.defineProperty with an object with a get and set properties. Or, with multiple properties, use Object.defineProperties to define them all at once.
One approach which will help with code organization is to not use lots of local storage keys, but instead use a single object that gets stored.
const props = ['logged', 'notificationsMuted'] as const;
const defaultStorage = Object.fromEntries(props.map(prop => [prop, false]));
const getStorage = () => JSON.parse(localStorage.getItem('settings') || JSON.stringify(defaultStorage));
const flags = Object.defineProperties(
{},
Object.fromEntries(
props.map(
prop => [
prop,
{
get: () => getStorage()[prop],
set: (newVal: boolean) => {
const store = getStorage();
store.prop = newVal;
localStorage.setItem('settings', JSON.stringify(store));
}
}
]
)
)
) as Record<(typeof props)[number], boolean>;
This is the current "best" solution I can come up with. Open to anyone who can provide an improvement over this:
function getter(prop: string): boolean {
return localStorage.getItem(prop) === "true";
}
function setter(prop: string, val: boolean): void {
if (val) {
localStorage.setItem(prop, "true");
} else {
localStorage.removeItem(prop);
}
}
const flags = {
get logged() { return getter("logged") },
set logged(val: boolean) { setter("logged", val) },
get notificationsMuted() { return getter("notifications-muted"); },
set notificationsMuted(val: boolean) { setter("notifications-muted", val); }
}
I'm using Vue 2 and in one of my components I've defined a simple computed property to filter through an array of objects:
computed: {
filteredPlans: {
get() {
let res = [];
if (this.query.length > 1) {
this.plans.forEach(p => {
if (JSON.stringify(p).includes(this.query)) res.push(p);
});
return res;
} else return this.plans;
},
set() {
return this.plans;
}
}
},
I want to using the same logic in 5 other components as well. So, my question is should I just duplicate the code on other compoents or make it available globaly, also how do i do that?
You can try creating a Mixins file and add the computed property in it.
Mixin named test-mixin
export default {
computed: {
filteredPlans: {
get() {
let res = [];
if (this.query.length > 1) {
this.plans.forEach(p => {
if (JSON.stringify(p).includes(this.query)) res.push(p);
});
return res;
} else return this.plans;
},
set() {
return this.plans;
}
}
},
}
This can be reused in any component by importing the mixin file like follows
import testMixin from 'test-mixin';
export default {
mixins: [testMixin]
}
Is it possible to use destructuring in an interface where a property depends on other one?
My arguments are the following:
interface PropsVariationA {
functionToExecute?: () => void;
executeExtraFunction: true;
}
interface PropsVariationB {
executeExtraFunction: false;
}
type Props = PropsVariationA | PropsVariationB;
So when I try to use it with destructuring I (obviously) get an error:
function SomeFunctionWithDestructuring({
executeExtraFunction,
functionToExecute = () => {} // Error: property 'functionToExecute' doesnt exist on type 'Props'
}: Props) {
if (executeExtraFunction) functionToExecute();
return null;
}
Check out this demo.
I managed to make it work without destructuring but I wonder if there is any way.
If you set functionToExecute to undefined in PropsVariationB then it would work:
interface PropsVariationA {
functionToExecute?: () => void;
executeExtraFunction: true;
}
interface PropsVariationB {
functionToExecute: undefined;
executeExtraFunction: false;
}
type Props = PropsVariationA | PropsVariationB;
// function without destructuring
function SomeFunction(props: Props) {
if (props.executeExtraFunction && props.functionToExecute)
props.functionToExecute();
return "a";
}
// function with destructuring
function SomeFunctionWithDestructuring({
executeExtraFunction,
functionToExecute = () => {} // Any way to do destructuing here?
}: Props) {
if (executeExtraFunction) functionToExecute();
return null;
}
// This error is right
SomeFunction({ executeExtraFunction: false, functionToExecute: () => {} });
I'm trying to make descriptor which can change return type in typescript but I don't know how to do this.
Here is the code and what I tried:
function changeReturnType()
{
return <T extends unknown>(
target: {},
key: string | symbol,
descriptor: TypedPropertyDescriptor<T>
) => {
const oldValue = descriptor.value;
return {
...descriptor,
value(...argv)
{
// #ts-ignore
return String(oldValue.call(target, ...argv))
}
}
};
}
class Foo {
#changeReturnType()
square ()
{
return 1;
}
}
let val = new Foo().square(); // I hope ts know here is string
console.dir({
val,
type: typeof val,
});
This is currently not possible in TypeScript but you may look at this comment and modify hack described there to your needs (i.e. for method decorator usage).
Is there a way in typescript to set a property name from a variable?
Something like this
export function objectFactory(prop: string) {
return {
prop: {
valid: false
}
};
}
You are looking for computed properties, this is an ES6 feature and not specific to TypeScript.
export function objectFactory(prop: string) {
return {
[prop]: {
valid: false
}
};
}
You can do it like this:
export function objectFactory(prop: string) {
let data: any = {};
data[prop] = {};
data[prop].valid = false;
return data;
}