我在Angular应用程序中编写了类似于以下内容的构造(这已经大大简化以演示该问题).什么会阻止在DemoSource类的项目实例上定义filterProperty()函数?
使用export关键字是因为每个构造都在单独的文件中定义.
export interface IProperty { filterProperty(): string; } export class Demo implements IProperty { displayName: string; filterProperty(): string { return this.displayName; } } export class DemoSource<TItem extends IProperty> { filterChange = new BehaviorSubject(''); filteredData: TItem[]; constructor(private service: IService<TItem>) { // A BehaviorSubject<Array<TItem>> from a service this.filteredData = service.data.value.slice(); } connect(): Observable<TItem[]> { return Observable.merge(this.service.data).map(() => { this.filteredData = this.service.data.value.slice().filter((item: TItem) => { // Object doesn't support property or method 'filterProperty' const searchStr = item.filterProperty().toLowerCase(); return searchStr.indexOf(this.filter.toLowerCase()) !== -1; }); return filteredData; }); } }
错误TypeError:对象不支持属性或方法’filterProperty’
更新
将IProperty契约函数从filter()更改为filterProperty()以避免混淆.
这是错误:
在这里,您可以看到项目实例如何正确填充其所有属性,但没有定义filterProperty()函数(它也不在proto中):
更新
以下是服务详情:
@Injectable() export class IdentityService implements IService<AppUser> { users = new BehaviorSubject<Array<AppUser>>([]); public get data(): BehaviorSubject<AppUser[]> { return this.users; } } export interface IService<T> { data: BehaviorSubject<T>; }
以下是来自浏览器的纯API调用的结果:
某些属性因其数据而被编辑
更新 – 透明的JavaScript
Object.defineProperty(exports,"__esModule",{ value: true }); var Demo = (function () { function Demo() {} Object.defineProperty(Demo.prototype,"filter",{ get: function () { return this.displayName; },enumerable: true,configurable: true }); return Demo; }()); exports Demo = Demo;
更新
Web App演示此问题:Typescript / Web API Interface Issue
Web应用程序的GitHub回购:typescript-interface-issues
数据来自JSON响应,是纯对象和数组的结构,只有在其原型Object和Array上定义的方法.
item实际上不是一个类实例,并且没有它应该具有的filterProperty方法.因此,考虑到IProperty应该具有filterProperty,指定DemoSource< IProperty>是不正确的.这使得TypeScript认为对象有这个方法,而他们没有它 – 它们仍然是普通对象,指定的类型不会改变它们.
用于泛型的接口应该反映数据结构属性(而不是方法).对于应该由普通对象构造的类,在构造函数中接受普通对象是一种好习惯:
export interface IItemData { displayName: string; id?: number; ... } export class Item implements IItemData { displayName: string; constructor({ displayName }: IItemData) { this.displayName = displayName; } filterProperty(): string { return this.displayName; } }
然后应处理数据结构,并将普通项转换为Item实例:
export class DemoSource<TItem extends IItemData> { ... this.filteredData = this.service.data.value.slice() .map((item: TItem) => { // item doesn't have 'filterProperty' return new Item(item); }) .filter((item: Item) => { // item has 'filterProperty' const searchStr = item.filterProperty().toLowerCase(); return searchStr.indexOf(this.filter.toLowerCase()) !== -1; }); ...