我正在尝试使用 DefinatelyTyped 干净地编写 Angular 自定义 $resource
扩展作为工厂作为 TypeScript 类。 (IResource
、IResourceClass
等)。
根据Misko Hevery资源只是构造函数
函数,因此我希望能够将我的$resource
定义为具有一些类型安全接口(interface)(INamedEntityResource
或INamedEntity
) 并混合服务定义,但我似乎无法在我的 NamedEntityResource 原型(prototype)上获取标准类方法以最终出现在工厂实例上。
有没有办法使用constructor()
函数来做到这一点,或者我应该放弃并只用纯JavaScript定义服务?
declare module EntityTypes {
interface INamedEntity { }
}
module Services {
export interface INamedEntitySvc {
Name(params: {}, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity;
Clear(params: {}, value: EntityTypes.INamedEntity, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity;
}
// WILL have correct interface definition for the resource
export interface INamedEntityResource extends NamedEntityResource, INamedEntitySvc { }
export class NamedEntityResource {
// #1 DOESN'T WORK - These are on NamedEntityResource.prototype but don't end up on svc
public someMethod() { }
public someOtherMethod() { }
constructor($resource) {
var paramDefaults = {
};
var svc: INamedEntitySvc = $resource(getUrl(), paramDefaults, {
Name: <any>{ method: "GET", params: { action: "Name" } },
Clear: <any>{ method: "PATCH", params: { action: "Clear" }, headers: { 'Content-Type': 'application/json' } },
});
// THIS WORKS - but it's not a NamedEntityResource
svc["prototype"].someMethod = function () { }
svc["prototype"].someOtherMethod = function () { }
return <any>svc;
// #1 DOESN'T WORK THOUGH
return; // doesn't pick up methods on prototype
// #2 THIS DOESN'T WORK EITHER
NamedEntityResource["prototype"] = angular.extend(this["prototype"] || {}, svc["prototype"]);
return this;
}
}
// Registration
var servicesModule: ng.IModule = angular.module('npApp.services');
servicesModule.factory('NamedEntityResource', NamedEntityResource);
}
进一步
因此,这样做的目的是让我能够编写一个资源类,其中的方法将在我通过 HTTP 加载的每个资源上进行注释。在本例中,是我的 INamedEntity
。
这是迄今为止我能找到的最接近的解决方案,它看起来确实有效,但感觉真的很糟糕。
module Services {
export interface INamedEntitySvc {
Name(params: {}, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity;
Clear(params: {}, value: EntityTypes.INamedEntity, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity;
}
// WILL have correct interface definition for the resource
export interface INamedEntityResource extends NamedEntityResource, INamedEntitySvc { }
export class NamedEntityResourceBase {
public someMethod() { }
public someOtherMethod() { }
}
// extend our resource implementation so that INamedEntityResource will have all the relevant intelisense
export class NamedEntityResource extends NamedEntityResourceBase {
constructor($resource) {
super(); // kind of superfluous since we're not actually using this instance but the compiler requires it
var svc: INamedEntitySvc = $resource(getUrl(), { }, {
Name: <any>{ method: "GET", params: { action: "Name" } },
Clear: <any>{ method: "PATCH", params: { action: "Clear" }, headers: { 'Content-Type': 'application/json' } },
});
// Mixin svc definition to ourself - we have to use a hoisted base class because this.prototype isn't setup yet
angular.extend(svc["prototype"], NamedEntityResourceBase["prototype"]);
// Return Angular's service (NOT this instance) mixed in with the methods we want from the base class
return <any>svc;
}
thisWontWork() {
// since we never actually get a NamedEntityResource instance, this method cannot be applied to anything.
// any methods you want have to go in the base prototype
}
}
// Registration
var servicesModule: ng.IModule = angular.module('npApp.services');
servicesModule.factory('NamedEntityResource', NamedEntityResource);
}
诀窍是;
- 将我想要的服务方法提升到基类中,因为在调用我的 constructor() 函数时 this.prototype 尚未初始化。
- 返回
svc
,它是来自构造函数的 Angular$resource
服务,当然你可以在 JavaScript 中执行此操作,但感觉就像在 TypeScript 中进行肮脏的鸭子打字. - 为了获取 svc.prototype 上的方法,我直接从我的基类扩展它。这特别令人讨厌,因为这意味着每次创建实例时都要设置原型(prototype)。
- 这个 sh** 三明治的最后一个刺鼻的香气是我必须在构造函数上调用 super() 来获取我要丢弃的实例,只是为了让它编译。</li>
但是,最后,我可以向 NamedEntityResourceBase
添加方法,它们将出现在从我的 HTTP 资源加载的所有实体的原型(prototype)中。
最佳答案
我一直在寻找这个问题的答案。它在 typescript 文档中。接口(interface)可以扩展类。向资源实例添加方法的解决方案如下:
class Project {
id: number;
title: string;
someMethod(): boolean {
return true;
}
}
export interface IProject extends ng.resource.IResource<IProject>, Project {
// here you add any method interface generated by the $resource
// $thumb(): angular.IPromise<IProject>;
// $thumb(params?: Object, success?: Function, error?: Function): angular.IPromise<IProject>;
// $thumb(success: Function, error?: Function): angular.IPromise<IProject>;
}
export interface IProjectResourceClass extends ng.resource.IResourceClass<IProject> { }
function projectFactory($resource: ng.resource.IResourceService): IProjectResourceClass {
var Resource = $resource<IProject>('/api/projects/:id/', { id: '@id' });
// the key, for this to actually work when compiled to javascript
angular.extend(Resource.prototype, Project.prototype);
return Resource;
}
module projectFactory {
export var $inject: string[] = ['$resource'];
}
我还没有完全测试,但我已经测试了一些并且可以工作。
关于angularjs - 在 TypeScript 中扩展 AngularJS $resource 工厂,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21819698/