类接口函数定义 - TypeError:对象不支持属性或方法

时间:2017-08-23 15:38:08

标签: angular typescript typescript2.0

我在Angular应用程序中编写了类似于以下内容的构造(这已经大大简化以演示问题)。什么会阻止在filterProperty()类的item实例中定义DemoSource函数?

使用了

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()以避免混淆。

这是错误:

error

在这里,您可以看到item实例如何正确填充其所有属性,但未定义filterProperty()函数(它不在 proto 中或者):

item

更新
以下是服务详情:

@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​​数据填充的服务:
service

以下是来自浏览器的纯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 App演示此问题:Typescript / Web API Interface Issue
Web应用程序的GitHub Repo:typescript-interface-issues

3 个答案:

答案 0 :(得分:3)

来自JSON响应的数据结果,是纯对象和数组的结构,只有在其原型var whereCallExpression = Expression.Call( typeof(Queryable), "Where", new Type[] { query.ElementType }, query.Expression, Expression.Quote(lambda)); Object上定义的方法。

Array不是一个真正的类实例,并且它没有item方法。因此,考虑到filterProperty应该具有DemoSource<IProperty>,指定IProperty是不正确的。这使得TypeScript认为对象有这种方法,而他们没有这种方法 - 它们仍然是普通的对象,指定的类型不会改变它们。

用于泛型的接口应该反映数据结构属性(而不是方法)。对于应该由普通对象构造的类,在构造函数中接受普通对象是一种很好的做法:

filterProperty

然后应该处理数据结构,并且应该将普通项转换为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

答案 1 :(得分:0)

我认为filter是保留关键字。因此,角度尝试在filter类型上实现Object函数。 filter只能用于Array。尝试为函数使用不同的名称。

如果不是这样的话。也许服务数据项应该是Object而不是TTem实例。我们只能在其实例上获取类方法。您可以尝试按如下方式创建新实例

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

答案 2 :(得分:0)

我能够按照我原先想要的方式使用以下设置。显然,关键是向C#对象添加filter属性,该对象用于通过Web API将数据序列化为JSON。不确定为什么需要这样做,因为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;
        });
    }
}