ts-preferences
Type-safe library for saving and loading preferences
Table of Contents
Installation
npm install ts-preferences --save
Why?
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
.
Usage
Basic Example
This rather artificial example shows how preferences can be used with full type-safety:
; ; // Initialize a preference manager:; // Replace title if corresponding preference is true:if Preferences.getP.replace_title ; // Randomize background-color if saved counter value is a multiple of 5:if counter % 5 === 0 // Save a new counter value:Preferences.setP.counter, counter + 1;
(A preference which is automatically changed on every page load probably doesn't make too much sense beyond demonstration purposes.)
P
Object
The get
, 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:
; ; ; ; Preferences.getP.foo; // OKPreferences.setP.foo, false; // OKPreferences.getforgedPreference; // throws exceptionPreferences.setforgedPreference, 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.
Preference Groups
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._
– aPreferencesObject
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:
;
In this case, you might do something like this in your application:
if Preferences.getP.video._.vsync
Error Handling
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:
; ; ;
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
:
;
Upgrading to v2
Initialization
init
is removed. Use thePreferenceManager
constructor instead.- Specifying a response handler is optional (defaults to
SIMPLE_RESPONSE_HANDLER
). - The provided
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:
; ;
v2:
; ;
Response Handler
Status.LOCALSTORAGE_ERROR
is renamed toStatus.STORAGE_ERROR
.
v1:
switch response.status { // ... case Status.LOCALSTORAGE_ERROR: // ...}
v2:
switch response.status { // ... case Status.STORAGE_ERROR: // ...}
Preference Dependencies
enabled
is renamed toshouldBeAvailable
.
v1:
Preferences.enabledp;
v2:
Preferences.shouldBeAvailablep;
HTML Menu Generation
- HTML menu generation is removed. (It was basically just function application anyway.)
v1:
;
v2:
;
API Reference
Every preference takes an argument of type PreferenceData<T>
, which for the different preference types has the properties listed below.
Preference
key: string
Used for saving preference values to localStorage
.
Must be unique for every preference.
label: string
User-readable label to be displayed in a generated GUI.
default: T
Default value for the preference.
description?: string
Optional 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.
StringPreference
multiline: boolean
Whether values may contain line breaks.
minLength?: number
Optional minimum length.
Defaults to 0
.
maxLength?: number
Optional maximum length.
Defaults to Infinity
.
RangePreference
min: number
Minimum allowed value.
max: number
Maximum allowed value.
MultichoicePreference
options: 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.