首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从界面编程创建类型记录用户定义的类型守卫

从界面编程创建类型记录用户定义的类型守卫
EN

Stack Overflow用户
提问于 2017-05-21 16:50:48
回答 1查看 452关注 0票数 3

在我的Angular2项目中,我为GridMetadata创建了一个接口

grid-metadata.ts

代码语言:javascript
复制
export interface GridMetadata {
  activity: string;
  createdAt: object;
  totalReps: number;
  updatedAt: object;
}

在我的服务中,我有一个公共方法create,它需要一个参数,它应该是一个具有字符串值的属性activity的对象,例如{ activity: 'Push-Ups' }。您可以在GridService的底部找到这个

grid.service.ts

代码语言:javascript
复制
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));
        });
    });
  }
}

问题1

首先,请注意,在我的create方法中,我已经说过所需的参数metadata应该与接口GridMetadata相匹配--但它不匹配。我只是向它传递一个对象{ activity: 'Push-Ups' },并在接口中注意到还有另外三个必需的属性(createdAttotalRepsupdatedAt)。

编译时或运行时都没有错误。为什么会这样呢?

我很可能只是误解了这一点,因为我是TypeScript新手。

问题2

我相信问题1应该有一个错误,这是可以很容易解决的,我只是好奇为什么不抛出错误。

我真正的问题是,您可以看到我继续构建metadata对象,通过添加缺少的属性(createdAttotalRepsupdatedAt)来匹配接口GridMetadata的签名。

然后,如果对象与接口不匹配,我将拒绝这个承诺。

如何检查createdAtupdatedAt不仅是对象,而且与签名{ .sv: 'timestamp' }相匹配,还有比我现在使用的if语句更好的方法来做到这一点呢?

问题3

最后,为了方便起见,是否有一种方法可以编程地从接口创建用户定义的类型保护程序?

我不会想象TypeScript眼中的接口不是一个对象,因此不能在函数中以编程方式处理,例如提取属性activity, createdAt, ...和值string, object, ...,并使用它们循环通过并在保护中进行编程检查。

虽然这里有三个问题,但实际上Q2是我最关心的问题,因为我确信Q1有一个简单的答案,而Q3只是加分--我将接受Q2的答案,并非常感谢您的帮助!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-05-21 19:14:48

问题1

类型记录编译器不会抱怨b/c,编译器没有查看HTML模板。

问题2

最后,它都是Javascript,所以如果不检查对象是否具有所需的属性,就无法验证对象的“签名”。例如,添加到原始逻辑中:

代码语言:javascript
复制
typeof obj.createdAt === 'object' &&
    obj.createdAt.hasOwnProperty('.sv') && 
    typeof obj.createdAt['.sv']  === 'string'

但是,我可能更倾向于首先检查作为对象的属性是否存在(而不是检查它们的类型):

代码语言:javascript
复制
if (!obj.createdAt || !obj.updatedAt) {
    return false;
}
// now check for the child properties
return obj.createdAt.hasOwnProperty('.sv') &&
    typeof obj.createdAt['.sv'] === 'string' &&
    ...

很明显,您可以用不同的结构来构造这个问题,但是所有这些都归结为相同的检查。

问题3

类型记录中的接口仅在编译时使用。它们从未包含在编译后的输出中。因此,在运行时,接口不存在,不能以编程方式访问。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44099420

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档