Programmatically Create Typescript Custom Interface Protection Types

In my Angular2 project, I created an interface for GridMetadata

:

Grid-metadata.ts

export interface GridMetadata {
  activity: string;
  createdAt: object;
  totalReps: number;
  updatedAt: object;
}

      

Inside my service, I have a public method create

that requires an argument, which must be a single property object activity

that has a String value, eg. { activity: 'Push-Ups' }

... You can find this at the bottom GridService

:

grid.service.ts

import { Injectable } from '@angular/core';

import {
  AngularFireDatabase,
  FirebaseListObservable
} from 'angularfire2/database';
import * as firebase from 'firebase/app';
// import { Observable } from 'rxjs/Observable';

import { AuthService } from './auth.service';
// TODO: Is this the correct order for interfaces?
import { GridMetadata } from './grid-metadata';

@Injectable()
export class GridService {
  private gridMetadata: FirebaseListObservable<any>;

  constructor(
    private afDb: AngularFireDatabase,
    private authService: AuthService
  ) {
    this.init();
  }

  private init(): void {
    this.gridMetadata = this.afDb.list('gridMetadata');
  }

  create(metadata: GridMetadata): Promise<any> {
    function isGridMetadata(obj: any): obj is GridMetadata {
      return typeof obj.activity === 'string' &&
        typeof obj.createdAt === 'object' &&
        typeof obj.totalReps === 'number' &&
        typeof obj.updatedAt === 'object' ?
          true :
          false;
    }

    return new Promise((resolve, reject) => {
      if (this.authService.isAuthenticated === false) {
        return reject(new Error('Can’t create new grid; you’re not auth’d.'));
      }

      let key: string = this.gridMetadata.push(undefined).key;
      let uri: string = [this.authService.currentUserId, key].join('/');

      metadata.createdAt = firebase.database.ServerValue.TIMESTAMP;
      metadata.totalReps = 0;
      metadata.updatedAt = firebase.database.ServerValue.TIMESTAMP;

      if (isGridMetadata(metadata) === false) {
        return reject(new TypeError('`metadata` doesn’t match the signature of the `GridMetadata` interface.'));
      }

      this.gridMetadata.update(uri, metadata)
        .then(() => {
          resolve();
        })
        .catch((error) => {
          reject(new Error(error.message));
        });
    });
  }
}

      

Question 1

First of all, notice that in my method, create

I said that the required argument metadata

must match an interface GridMetadata

that doesn't exist. I pass it Object { activity: 'Push-Ups' }

and notice in the interface that there are three more required properties ( createdAt

, totalReps

and updatedAt

).

No errors occur at compile time or runtime. Why is this?

I most likely just misunderstood this as I am new to TypeScript.

Question 2

I believe there must be a mistake in question 1; this can be easily fixed. I'm just wondering why the error isn't being raised.

My real question is, you can see that I was constructing an object metadata

to match the interface signature GridMetadata

by adding the missing properties ( createdAt

, totalReps

and updatedAt

).

Then I reject the promise if the object doesn't match the interface.

How can I check that createdAt

and updatedAt

are not only objects but also match the signature { .sv: 'timestamp' }

, and is there a better way to do this than the operator if

I'm using now?

Question 3

Finally; for convenience, is there a way to programmatically create a user protected Guard type from an interface?

I would not perceive an image differently as an interface in the eyes of TypeScript is not an object, so it cannot be handled programmatically in a function, for example. extract properties activity, createdAt, ...

and values string, object, ...

and use them for scrolling and programmatically checking in protection.


While there are three questions here, Q2 is actually my main problem as I am sure Q1 has a simple answer and Q3 only has bonus points - I will only accept answers for Q2 and really appreciate your help!

+3


source to share


1 answer


Question 1

The Typescript compiler doesn't complain to b / c that the compiler doesn't look at your HTML templates.

Question 2

After all, this is all Javascript, so there is no way to check the "signature" of an object without checking if the object has the required properties. For example, adding the original logic:

typeof obj.createdAt === 'object' &&
    obj.createdAt.hasOwnProperty('.sv') && 
    typeof obj.createdAt['.sv']  === 'string'

      



But I might be more inclined to check first for the existence of properties that are objects (rather than checking their type):

if (!obj.createdAt || !obj.updatedAt) {
    return false;
}
// now check for the child properties
return obj.createdAt.hasOwnProperty('.sv') &&
    typeof obj.createdAt['.sv'] === 'string' &&
    ...

      

Obviously you could structure it differently, but it all boils down to the same kinds of checks.

Question 3

Interfaces in Typescript are used only at compile time. They are never included in the compiled output. Therefore, at run time, interfaces do not exist and cannot be accessed programmatically.

0


source







All Articles