interface SearchResult < T > { score : number ; value : T ; } type SearchScoringStrategy < T > = ( item : T , query : string ) => boolean ; export class SearchModel < T > { private scoringStrategies : { score : number ; strategy : SearchScoringStrategy < T > ; } [ ] = [ ] ; registerScoringStrategy ( score , strategy ) { this . scoringStrategies . push ( { score , strategy } ) ; return this ; } search ( list , query ) { const result = [ ] ; for ( const item of list ) { const score = this . getItemScore ( item , query ) ; if ( score > 0 ) { result . push ( { score , value : item } ) ; } } this . sortResultWithScore ( result ) ; return result . map ( r => r . value ) ; } private getItemScore ( item , query ) { let score = 0 ; this . scoringStrategies . forEach ( ( scoring ) => { if ( scoring . strategy ( item , query ) ) { score += scoring . score ; } } ) ; return score ; } private sortResultWithScore ( result ) { result . sort ( ( a , b ) => { if ( a . score > b . score ) { return - 1 ; } else if ( a . score < b . score ) { return 1 ; } return 0 ; } ) ; } }