在我的Angular2项目中,我为GridMetadata
创建了一个接口:
export interface GridMetadata {
activity: string;
createdAt: object;
totalReps: number;
updatedAt: object;
}
在我的服务中我有一个公共方法create
,它需要一个参数,该参数应该是一个具有一个属性activity
且具有字符串值的对象,例如{ activity: 'Push-Ups' }
。您可以在GridService
:
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));
});
});
}
}
首先请注意,在我的create
方法中,我已经说过必需的参数metadata
应该与接口GridMetadata
相匹配 - 它没有。我只传递了一个对象{ activity: 'Push-Ups' }
,并在界面中注意到还有其他三个必需属性(createdAt
,totalReps
和updatedAt
)。
编译时或运行时都没有错误。这是为什么?
我很可能只是误解了这一点,因为我是TypeScript的新手。
我相信问题1应该有错误;这可以很容易地修复我只是好奇为什么没有抛出错误。
我真正的问题是你可以看到我继续构建metadata
对象以匹配接口GridMetadata
的签名,方法是添加缺少的属性(createdAt
,{{1} }和totalReps
)。
如果对象与接口不匹配,我会拒绝Promise。
如何检查updatedAt
和createdAt
不仅是对象,还匹配签名updatedAt
,是否有更好的方法来执行此操作{ .sv: 'timestamp' }
声明我现在正在使用?
最后;为方便起见,有一种方法可以从接口以编程方式创建用户定义的类型保护吗?
我不会成像,因为TypeScript眼中的界面不是一个对象,因此无法在函数中以编程方式处理,例如,提取属性if
和值activity, createdAt, ...
并使用这些来循环并以编程方式检查后卫。
虽然这里有三个问题,但实际上Q2是我最关心的问题,因为我确信Q1有一个简单的答案而Q3只是奖励积分 - 我会接受Q2的答案,非常感谢你的帮助!
答案 0 :(得分:0)
问题1
Typescript编译器没有抱怨b / c编译器没有查看你的HTML模板。
问题2
最后它是所有Javascript,因此没有办法验证对象的“签名”而不检查对象是否具有所需的属性。例如,添加到原始逻辑:
typeof obj.createdAt === 'object' &&
obj.createdAt.hasOwnProperty('.sv') &&
typeof obj.createdAt['.sv'] === 'string'
但我可能更倾向于首先检查是否存在作为对象的属性(而不是检查它们的类型):
if (!obj.createdAt || !obj.updatedAt) {
return false;
}
// now check for the child properties
return obj.createdAt.hasOwnProperty('.sv') &&
typeof obj.createdAt['.sv'] === 'string' &&
...
你显然可以采用不同的结构,但这一切都归结为相同类型的检查。
问题3
Typescript中的接口仅在编译时使用。它们永远不会包含在编译输出中。因此,在运行时,接口不存在,无法以编程方式访问。