Class Interface function definition - TypeError: Object doesn't support property or method
Class Interface function definition - TypeError: Object doesn't support property or method
我在 Angular 应用程序中编写了一个类似于以下的结构(为了演示问题,这已被大大简化)。什么会阻止 filterProperty()
函数在 DemoSource
class 内部的 item
实例上定义?
使用 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;
});
}
}
在调用 item.filter()
的位置进行调试时,出现以下错误:
ERROR TypeError: Object doesn't support property or method 'filterProperty'
更新
将 IProperty
合约函数从 filter()
更改为 filterProperty()
以避免混淆。
这是错误:
在这里,您可以看到 item
实例如何正确填充其所有属性,但没有定义 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>;
}
某些属性因其数据已被编辑
更新 - 转译 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 应用程序:Typescript / Web API Interface Issue
GitHub Web 应用程序的回购:typescript-interface-issues
我认为 filter
是保留关键字。因此 angular 试图在类型 Object
上实现 filter
函数。 filter
只能用于 Array
。尝试为函数使用不同的名称。
如果不是这样。也许服务数据项应该作为 Object
而不是 TTem
instance.We 只能在其实例上获得 class 方法。您可以尝试按如下方式创建新实例吗
this.filteredData = this.service.data.value.slice().filter(
(item: TItem) => {
// Object doesn't support property or method 'filter'
let instance: TItem = new TItem();
Object.assign(instance, item);
const searchStr = instance.filter().toLowerCase();
return searchStr.indexOf(this.filter.toLowerCase()) !==-1;
});
通过以下设置,我能够按照我最初希望的方式运行它。显然,关键是向用于通过 Web API 将数据序列化为 JSON 的 C# 对象添加 filter
属性。不确定为什么需要这样做,因为 TypeScript 应该能够扩展从 Web API 接收到的模型并具有额外的功能。
示例简化以演示问题集
DemoModel.cs
// C# Class JSON is serialized from via Web API
public class DemoModel
{
public string displayName { get; set; }
public string filter
{
get { return displayName; }
}
}
iproperty.interface.ts
export interface IProperty {
filter: string;
}
demo.model.ts
import { IProperty } from '../interfaces/iproperty.interface';
export class Demo implements IProperty {
displayName: string;
get filter(): string { return this.displayName; }
}
core.datasource.ts
export class CoreDataSource<TItem extends IProperty> {
filterChange = new BehaviorSubject('');
filteredData: TItem[];
get filter(): string { return this.filterChange.value; }
set filter(filter: string) { this.filterChange.next(filter); }
constructor(private service: IService<TItem>) {
super();
this.filteredData = service.data.value.slice();
}
connect(): Observable<TItem[]> {
const displayDataChanges = [
this.service.data,
this.filterChange
];
return Observable.merge(...displayDataChanges).map(() => {
this.filteredData = this.service.data.value.slice().filter((item: TItem) => {
const searchStr = item.filter.toLowerCase();
return searchStr.indexOf(this.filter.toLowerCase()) !== -1;
});
return filteredData;
});
}
}
数据来自 JSON 响应,是一种普通对象和数组的结构,仅具有在其原型上定义的方法,Object
和 Array
。
item
并不是真正的 class 实例,也没有它应该有的 filterProperty
方法。所以指定 DemoSource<IProperty>
是不正确的,因为 IProperty
应该有 filterProperty
。这让 TypeScript 误以为对象有这个方法,而实际上它们没有——它们仍然是普通对象,指定的类型不会改变它们。
用于泛型的接口应该反映数据结构属性(而不是方法)。对于应该由普通对象构造的 classes,最好在构造函数中接受普通对象:
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;
});
...
我在 Angular 应用程序中编写了一个类似于以下的结构(为了演示问题,这已被大大简化)。什么会阻止 filterProperty()
函数在 DemoSource
class 内部的 item
实例上定义?
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;
});
}
}
在调用 item.filter()
的位置进行调试时,出现以下错误:
ERROR TypeError: Object doesn't support property or method 'filterProperty'
更新
将 IProperty
合约函数从 filter()
更改为 filterProperty()
以避免混淆。
这是错误:
在这里,您可以看到 item
实例如何正确填充其所有属性,但没有定义 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>;
}
某些属性因其数据已被编辑
更新 - 转译 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 应用程序:Typescript / Web API Interface Issue
GitHub Web 应用程序的回购:typescript-interface-issues
我认为 filter
是保留关键字。因此 angular 试图在类型 Object
上实现 filter
函数。 filter
只能用于 Array
。尝试为函数使用不同的名称。
如果不是这样。也许服务数据项应该作为 Object
而不是 TTem
instance.We 只能在其实例上获得 class 方法。您可以尝试按如下方式创建新实例吗
this.filteredData = this.service.data.value.slice().filter(
(item: TItem) => {
// Object doesn't support property or method 'filter'
let instance: TItem = new TItem();
Object.assign(instance, item);
const searchStr = instance.filter().toLowerCase();
return searchStr.indexOf(this.filter.toLowerCase()) !==-1;
});
通过以下设置,我能够按照我最初希望的方式运行它。显然,关键是向用于通过 Web API 将数据序列化为 JSON 的 C# 对象添加 filter
属性。不确定为什么需要这样做,因为 TypeScript 应该能够扩展从 Web API 接收到的模型并具有额外的功能。
示例简化以演示问题集
DemoModel.cs
// C# Class JSON is serialized from via Web API
public class DemoModel
{
public string displayName { get; set; }
public string filter
{
get { return displayName; }
}
}
iproperty.interface.ts
export interface IProperty {
filter: string;
}
demo.model.ts
import { IProperty } from '../interfaces/iproperty.interface';
export class Demo implements IProperty {
displayName: string;
get filter(): string { return this.displayName; }
}
core.datasource.ts
export class CoreDataSource<TItem extends IProperty> {
filterChange = new BehaviorSubject('');
filteredData: TItem[];
get filter(): string { return this.filterChange.value; }
set filter(filter: string) { this.filterChange.next(filter); }
constructor(private service: IService<TItem>) {
super();
this.filteredData = service.data.value.slice();
}
connect(): Observable<TItem[]> {
const displayDataChanges = [
this.service.data,
this.filterChange
];
return Observable.merge(...displayDataChanges).map(() => {
this.filteredData = this.service.data.value.slice().filter((item: TItem) => {
const searchStr = item.filter.toLowerCase();
return searchStr.indexOf(this.filter.toLowerCase()) !== -1;
});
return filteredData;
});
}
}
数据来自 JSON 响应,是一种普通对象和数组的结构,仅具有在其原型上定义的方法,Object
和 Array
。
item
并不是真正的 class 实例,也没有它应该有的 filterProperty
方法。所以指定 DemoSource<IProperty>
是不正确的,因为 IProperty
应该有 filterProperty
。这让 TypeScript 误以为对象有这个方法,而实际上它们没有——它们仍然是普通对象,指定的类型不会改变它们。
用于泛型的接口应该反映数据结构属性(而不是方法)。对于应该由普通对象构造的 classes,最好在构造函数中接受普通对象:
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;
});
...