Posted under Angular & TypeScript & Web
Permalink
Tags Gotcha, Tip
I was using a JSON file to hold external configuration for an agular application, using the pattern detailed previously here, and the loading mechanism detailed here.
The configuration pattern used allowed parameters to be overridden dynamically from query string parameters, so that for example you could choose an application theme in your URL.
I wanted to further extend this to allow sticky configuration parameters using localStorage, in particular to support a sticky pinnable title banner as detailed previously here. The aim was that both the query string support and the sticky localStorage support would be encapsulated in the configuration pattern and all transparently handled by the existing configuration service. The service would be enhanced with a new method call to allow persisting of a sticky parameter to localStorage and to the loaded config, for example for my sticky pinnable banner, to allow calling to persist the new pin state when the pin state was changed via the pin button. The previous pin state would then be loaded when the site was subsequently visited. Sticky parameters would override settings in the JSON file, and query string parameters would further override both of the former, and a query string parameter would also be sticky and would persist to localStorage, so for example the previous theme selected in the URL would be remembered.
One challenge was that whilst the configuration structure conformed to a Typescript interface, I needed to dynamically update configuration parameters from query string parameters or localStorage. This meant using dynamic keys e.g. based on the querystring parameters. Whilst this was initially possible by casting the config structure and using a map style lookup with config[keyVariable], it was not possible to dynamically infer the type of a configuration parameter from the interface, noting that both query string and localStorage parameters are strings. Often this is not a problem, as the default javascript behaviour will quite often cope, but therein lies a danger. In one case with a boolean, my query string/localStorage parameter was of course a string, and when written to the configuration using a dynamic key name as above, it was written as a string rather than the boolean expected in the interface. This meant that the value ‘false’ was in fact interpreted as true, due to the standard javascript truthy behaviour whereby a non empty string counts as true.
In the end, my solution was to have a subsection in the configuration, called dynamicParams, which contained all these dynamic parameters. It was therefore a requirement that all the dynamic parameters were strings and that client code reading the configuration had to be aware of that and convert as required.
One useful feature was to use Typescript intersection types for the dynamic parameters section, as detailed here. This allowed me to specify the dynamic parameter names explicitly, to allow them to be read in code directly as for example using config.dynamicParams.theme. I could also use programmatic access as above using config[keyVariable].
The section of the interface definition to do this was as follows:-
export interface AppConfig {
appTitle: string;
description: string;/* These parameters can be overridden from URL query parameters
* and/or sticky local storage parameters
* They must all be strings as are accessed by key programmatically */
dynamicParams: {[key: string]: string | undefined} &
{
theme: string;
pinBanner?: string;
profileId?: string;
};
propertyDetail: {
defaults: {
photoLinkHoverText: string;
}
…
A particular point of note was that the intersection type using “&” was needed for this to work, rather than the union type “|”.
This solved the problem satisfactorily and all worked fine.
Comments Off on Dynamic Typing issues with Typescript/Angular 11