Verify application configuration from JSON file with matching type using TypeScript in an Angular project To bootstrap our application we have a configuration stored in a JSON file. This config holds different settings, like definition of endpoints (e. g. an API or SMS gateway etc.) or some default values for login.
endpoint.ts
export const isEndpoint = (candidate: any): candidate is Endpoint =&gt; {
  return (
    candidate &amp;&amp; typeof candidate === 'object' &amp;&amp;
    candidate.hasOwnProperty('protocol') &amp;&amp; (candidate.protocol === null || ['http', 'https'].includes(candidate.protocol)) &amp;&amp;
    candidate.hasOwnProperty('address') &amp;&amp; (candidate.address === null || typeof candidate.address === 'string') &amp;&amp;
    candidate.hasOwnProperty('port') &amp;&amp; (candidate.port === null || typeof candidate.port === 'number') &amp;&amp;
    candidate.hasOwnProperty('path') &amp;&amp; (candidate.path === null || typeof candidate.path === 'string')
  );
}

export interface Endpoint {
  protocol: 'http'|'https'|null,
  address: string|null,
  port: number|null,
  path: string|null,
}

configutation.ts
import { Endpoint, isEndpoint } from './endpoint';

export const isConfiguration = (candidate: any): candidate is Configuration =&gt; {
  return (
    candidate &amp;&amp; typeof candidate === 'object' &amp;&amp;
    candidate.hasOwnProperty('endpoints') &amp;&amp;
      candidate.endpoints.hasOwnProperty('api') &amp;&amp; isEndpoint(candidate.endpoints.api) &amp;&amp;
      candidate.endpoints.hasOwnProperty('smsGateway') &amp;&amp; isEndpoint(candidate.endpoints.smsGateway) &amp;&amp;
    candidate.hasOwnProperty('auth') &amp;&amp;
      candidate.auth.hasOwnProperty('company') &amp;&amp; (candidate.auth.company === null || typeof candidate.auth.company === 'string')
  );
}

export interface Configuration {
  endpoints: {
    api: Endpoint,
    smsGateway: Endpoint
  },

  auth: {
    company: string|null
  }
}

In the end the code is used in an Angular service like this:
get configuration$(): Observable&lt;Configuration&gt; {
  if (this.configutation$) {
    return this.configutation$;
  }

  const path = environment.configuration;

  this.configutation$ = this.http.get&lt;any&gt;(path).pipe(
    map(configutation =&gt; {   
      if (isConfigutation(configutation)) {
        return configutation;
      }

      throw new Error('Configutation is broken.');
    }),
    shareReplay(1),
    catchError(error =&gt; {
      console.log(error);
      return of(null);
    })
  );

  return this.configutation$;
}

Now the isEndpoint and isConfiguration methos seems very cumbersome, especially when the configuration grows. I was thinking of using JSON schema and validate the input against the schema file. But if somebody replaces that file for example during build, then it still could go wrong.
Can this be improved?
Is this the TypeScrpt way or how could this be improved?
If you're worried that someone may replace your schema during build, you should also be worried that someone might replace any other file as well. That's pure paranoia...
I would say the typescript way is to write the config in typescript as well. If that's not an option, then sure go for schema validation.
Anyway for your current implementation, there is a quite a lot that can be simplified in those conditions.
This:
candidate &amp;&amp; typeof candidate === 'object'

can be replaced with just
typeof candidate === 'object'

As there is no falsy value of type object.
Further those hasOwnProperty checks are not necesary. Deserialized json will have all those properties belong to their own. And if somebody passes an object that was not created by parsing json, but let's say instantiating a class, do you really care if those properties belong to the instance, the class or its parent?
export const isConfiguration = (candidate: any): candidate is Configuration =&gt; {
  return (
    typeof candidate === 'object' &amp;&amp;
    typeof candidate.endpoints === 'object' &amp;&amp;
    isEndpoint(candidate.endpoints.api) &amp;&amp;
    isEndpoint(candidate.endpoints.smsGateway) &amp;&amp;
    typeof candidate.auth === 'object' &amp;&amp;
    (candidate.auth.company === null || typeof candidate.auth.company === 'string')
  )
}