
Security News
OWASP 2025 Top 10 Adds Software Supply Chain Failures, Ranked Top Community Concern
OWASP’s 2025 Top 10 introduces Software Supply Chain Failures as a new category, reflecting rising concern over dependency and build system risks.
ts-preferences
Advanced tools
Type-safe library for saving and loading preferences
npm install ts-preferences --save
ts-preferences gives you an easy, safe way of defining and accessing preferences for your application, without a lot of boilerplate code.
You can only set and get preferences that actually exist, so no more hassle with preference keys.
And when requesting a preference value, you can always trust that you will get something and that it will have the right type, even if something goes wrong with localStorage.
This rather artificial example shows how preferences can be used with full type-safety:
import { BooleanPreference, IntegerPreference, PreferenceManager } from "ts-preferences";
const P = {
replace_title: new BooleanPreference({
key: "replace_title",
label: "Replace title",
default: true,
description: "Replace boring page title",
}),
counter: new IntegerPreference({
key: "counter",
label: "Counter",
default: 0,
description: "Weird counter thingy",
}),
};
// Initialize a preference manager:
const Preferences = new PreferenceManager(P, "my-awesome-app-");
// Replace title if corresponding preference is true:
if (Preferences.get(P.replace_title)) {
document.title = "top kek";
}
const counter = Preferences.get(P.counter);
// Randomize background-color if saved counter value is a multiple of 5:
if (counter % 5 === 0) {
document.body.style.backgroundColor = `rgb(${upTo(255)}, ${upTo(255)}, ${upTo(255)})`;
}
// Save a new counter value:
Preferences.set(P.counter, counter + 1);
function upTo(max: number): number {
return Math.round(Math.random() * max);
}
(A preference which is automatically changed on every page load probably doesn't make too much sense beyond demonstration purposes.)
P Objectget, set and reset work as expected if and only if p is in the PreferencesObject used to create the PreferenceManager.
That is, you can use all preferences in P, and only those, when talking to ts-preferences.
The following code compiles, but crashes:
import { BooleanPreference, PreferenceManager } from "ts-preferences";
const forgedPreference = new BooleanPreference({
key: "foo",
label: "foo label",
default: true,
});
const P = {
foo: new BooleanPreference({
key: "foo",
label: "foo label",
default: true,
}),
};
const Preferences = new PreferenceManager(P, "my-awesome-app-");
Preferences.get(P.foo); // OK
Preferences.set(P.foo, false); // OK
Preferences.get(forgedPreference); // throws exception
Preferences.set(forgedPreference, false); // throws exception
(Note that, although forgedPreference and P.foo are identical, they are not the same object, which is what counts in this case.)
You should only use members of your P object as input to get, set and reset.
If your editor supports TypeScript, it will autocomplete available preferences for you when you type e.g. Preferences.get(P._).
You may of course give your P object any name you want.
A PreferencesObject can contain not only preferences, but also preference groups.
A group is simply an object with these properties:
label – a label for the group._ – a PreferencesObject representing the group.dependencies? – a list of dependencies for the group.extras? – optional object that can be used for anything.An example of grouped preferences:
const P = {
video: {
label: "Video Settings",
_: {
vsync: new BooleanPreference({
key: "video_vsync",
label: "V-Sync",
default: false,
}),
textures: new MultichoicePreference({
key: "video_textures",
label: "Texture Quality",
default: 2,
options: [
{ value: 1, label: "Low", },
{ value: 2, label: "Medium", },
{ value: 3, label: "High", },
],
}),
},
},
audio: {
label: "Audio Settings",
_: {
doppler: new BooleanPreference({
key: "audio_doppler",
label: "Doppler Effect",
default: true,
}),
},
},
};
In this case, you might do something like this in your application:
if (Preferences.get(P.video._.vsync)) {
// ...
}
Things can go wrong when getting or setting preferences.
For example, localStorage may not be accessible, or the string saved therein may not parse to a value of the expected type.
To take care of these cases in a graceful way, define a response handler and give it as an argument to the PreferenceManager constructor.
Here is a very basic example:
import { AllowedTypes, PreferenceManager, RequestSummary, Response, Status } from "ts-preferences";
const P = {
// ...
};
const Preferences = new PreferenceManager(P, "my-awesome-app-", loggingResponseHandler);
function loggingResponseHandler<T extends AllowedTypes>(summary: RequestSummary<T>, preferences: PreferenceManager): Response<T> {
const response = summary.response;
switch (response.status) {
case Status.OK:
break;
default:
console.warn(`There was an error with preference '${summary.preference.key}'.`);
}
return response;
}
If you don't define a response handler, you will get no indication whatsoever if something goes wrong (but you will get valid preference values).
If you want to use another response handler for a specific transaction, you can use getWith or setWith:
const value = Preferences.getWith(loggingResponseHandler, P.foo);
init is removed. Use the PreferenceManager constructor instead.SIMPLE_RESPONSE_HANDLER).localStorage prefix is used as is (i.e. "-preference-" is not appended anymore). You should append it yourself so your users' saved preferences are not reset.v1:
import * as TSPreferences from "ts-preferences";
const Preferences = TSPreferences.init(
P,
"my-awesome-app",
TSPreferences.SIMPLE_RESPONSE_HANDLER,
);
v2:
import { PreferenceManager } from "ts-preferences";
const Preferences = new PreferenceManager(
P,
"my-awesome-app-preference-", // NB: "-preference-" appended!
);
Status.LOCALSTORAGE_ERROR is renamed to Status.STORAGE_ERROR.v1:
switch (response.status) {
// ...
case Status.LOCALSTORAGE_ERROR:
// ...
}
v2:
switch (response.status) {
// ...
case Status.STORAGE_ERROR:
// ...
}
enabled is renamed to shouldBeAvailable.v1:
Preferences.enabled(p);
v2:
Preferences.shouldBeAvailable(p);
v1:
const menu = Preferences.htmlMenu(generator);
v2:
const menu = generator(P);
Every preference takes an argument of type PreferenceData<T>, which for the different preference types has the properties listed below.
Preferencekey: stringUsed for saving preference values to localStorage.
Must be unique for every preference.
label: stringUser-readable label to be displayed in a generated GUI.
default: TDefault value for the preference.
description?: stringOptional user-readable description to be displayed in a generated GUI.
Defaults to "".
constraints?: Constraint<T>[]Optional list of constraints that preference values must satisfy, in addition to any constraints included in the preference class. Each constraint must be an object with these properties:
requirement: (value: T) => boolean – the predicate that values must satisfy.message: (value: T) => string – an error message for when a value does not satisfy the predicate.dependencies?: Dependency<any>[]Optional list of dependencies that can be used to indicate a dependency relation between preferences, which in turn can be used to enable or disable a preference in the GUI based on the values of other preferences. Each dependency must be an object with these properties:
preference: Preference<T> – the preference depended on.condition: (value: T) => boolean – a predicate that the value of that preference must satisfy.extras?: { readonly [key: string]: any }Optional object that can be used for anything, for example styling a single preference. Should be used with great care because it has no type-safety at all.
StringPreferencemultiline: booleanWhether values may contain line breaks.
minLength?: numberOptional minimum length.
Defaults to 0.
maxLength?: numberOptional maximum length.
Defaults to Infinity.
RangePreferencemin: numberMinimum allowed value.
max: numberMaximum allowed value.
MultichoicePreferenceoptions: MultichoicePreferenceOption<T>[]A list of available options. Must contain at least two elements. Each element must have these properties:
label: string – a user-readable label for the option.value: T – the value represented by the option.FAQs
localStorage-based preference manager for web apps, userscripts etc
We found that ts-preferences demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OWASP’s 2025 Top 10 introduces Software Supply Chain Failures as a new category, reflecting rising concern over dependency and build system risks.

Research
/Security News
Socket researchers discovered nine malicious NuGet packages that use time-delayed payloads to crash applications and corrupt industrial control systems.

Security News
Socket CTO Ahmad Nassri discusses why supply chain attacks now target developer machines and what AI means for the future of enterprise security.