Class Interface function definition - TypeError: Object doesn't support property or method

前端 未结 3 684
生来不讨喜
生来不讨喜 2020-12-12 07:08

I\'ve written a construct similar to the following in an Angular app (this has been greatly simplified to demonstrate the issue). What would prevent the filte

相关标签:
3条回答
  • 2020-12-12 07:27

    Data results from JSON response and is a structure of plain objects and arrays that only have methods that are defined on their prototypes, Object and Array.

    item isn't really a class instance and doesn't have filterProperty method it's supposed to have. So it's incorrect to specify DemoSource<IProperty>, considering that IProperty is supposed to have filterProperty. This fools TypeScript into thinking that objects have this method, while they don't have it - they are still plain objects, specified types don't change them.

    An interface that is used for generic is supposed to reflect data structure properties (not methods). For classes that are supposed to be constructed from plain objects it's a good practice to accept plain object in constructor:

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

    Then data structure should be processed and plain items should be converted to Item instances:

    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;
        });
        ...
    
    0 讨论(0)
  • 2020-12-12 07:28

    I was able to get it working the way I had originally wanted it to with the below setup. The key was, apparently, adding a filter property to the C# object that was being used to serialize data to JSON via Web API. Not sure why this would be required, as TypeScript should be able to extend the model received from Web API with additional functionality.

    Example simplified to demonstrate the problem set

    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;
            });
        }
    }
    
    0 讨论(0)
  • 2020-12-12 07:50

    As I think filter is reserved keyword. So that angular trying to implement filter function on type Object. filter can be used only for Array . try using different name for the function.

    If this is not the case. Maybe the service data item should come as Object instead of TTem instance.We can get the class methods only on its instances. Can you try creating new instance as follows

    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;
            });
    
    0 讨论(0)
提交回复
热议问题