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>;
}

这是使用来自 API 的数据填充的服务:

这是来自浏览器的纯 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 应用程序: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 响应,是一种普通对象和数组的结构,仅具有在其原型上定义的方法,ObjectArray

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;
    });
    ...