2015-04-13 5 views
5

ObjectiveМашинопись интерфейс свойство строки

У меня есть интерфейс Машинопись:

interface IInterface{ 
    id: number; 
    name: string; 
} 

У меня есть некоторые методы, которые принимают в записи имя свойства (строка).

Ex:

var methodX = (property: string, object: any) => { 
    // use object[property] 
}; 

Моя проблема заключается в том, что, когда я называю methodX, я должен написать имя свойства в строке.

Ex:methodX("name", objectX);где ObjectX реализует IInterface

Но это BAD: Если я переименовать свойство (скажем, я хочу, чтобы переименовать name в lastname) мне придется обновлять вручную все мои код.

И я не хочу эту зависимость.

Поскольку интерфейсы типа ввода не имеют реализаций JS, я не вижу, как я не мог использовать строку.

Я хочу иметь что-то вроде: methodX(IInterface.name.propertytoString(), objectX);

Я довольно новыми для JS, вы видите альтернативу?

(необязательно) Дополнительная информация: Зачем мне нужно передавать свойства как параметр и почему я не использую общий метод?

Я использую методы, которые связывают данные:

linkData = <TA, TB>(
    inputList: TA[], 
    inputId: string, 
    inputPlace: string, 
    outputList: TB[], 
    outputId: string) => { 

    var mapDestinationItemId: any = {}; 
    var i: number; 
    for (i = 0; i < outputList.length; ++i) { 
     mapDestinationItemId[outputList[i][outputId]] = outputList[i]; 
    } 

    var itemDestination, itemSource; 
    for (i = 0; i < inputList.length; ++i) { 
     itemDestination = inputList[i]; 
     itemSource = mapDestinationItemId[itemDestination[inputId]]; 
     if (itemSource) { 
      itemDestination[inputPlace] = itemSource; 
     } 
    } 
}; 

Но TA и TB может иметь много различных идентификаторов. Поэтому я не вижу, как сделать его более общим.

ответ

6

basarat Ответ - хорошая идея, но она не работает с интерфейсами.

Вы не можете написать methodX(interfacePropertyToString(()=>interfaceX.porpertyname), objectX), потому что interfaceX не является объектом.

Интерфейсы являются абстракциями, и они используются только для TypeScript, они не существуют в Javascript.

Но благодаря его ответу я нашел решение: с использованием параметра в методе.

Наконец, мы имеем:

interfacePropertyToString = (property: (object: any) => void) => { 
     var chaine = property.toString(); 
     var arr = chaine.match(/[\s\S]*{[\s\S]*\.([^\.; ]*)[ ;\n]*}/); 
     return arr[1]; 
    }; 

Мы должны использовать [\s\S], чтобы быть в состоянии соответствовать на мультилинии, потому что Машинопись конвертировать (object: Interface) => {object.code;} в многострочную функцию.

Теперь вы можете использовать его, как вы хотите:

 interfacePropertyToString((o: Interface) => { o.interfaceProperty}); 
     interfacePropertyToString(function (o: Interface ) { o.interfaceProperty}); 
+0

Великий ответ! Не могли бы вы немного рассказать о том, как TypeScript имеет дело с интерфейсами, определенными пользователем и с чем он их компилирует? Потому что я ничего не нашел об этом. Спасибо! –

+0

Кроме того, можете ли вы придумать какой-либо способ извлечь все свойства интерфейса таким образом? Благодаря! –

+0

@ radu-matei Интерфейс TypeScript не переводится в Javascript. Это просто не существует в Javascript. – Machtyn

2

Вы могли бы написать функцию, чтобы разобрать тело функции, чтобы найти имя, например:

methodX(getName(()=>something.name), objectX) 

Где getName будет делать toString на теле функции, чтобы получить строку вида "function(){return something.name}", а затем разобрать он должен получить "name".

Примечание: однако это имеет тенденцию ломаться в зависимости от того, как вы его минимизируете.

0

Несколько связанной с проблемой - как получить/установить значение на путь свойства. Я написал два класса для этого:

export class PropertyPath { 
    static paths = new Map<string, PropertyPath>() 

    static get<T, P>(lambda: (prop:T) => P) : PropertyPath { 
     const funcBody = lambda.toString(); 
     var ret : PropertyPath = this.paths[funcBody]; 
     if (!ret) { 
      const matches = funcBody.match(/(?:return[\s]+)(?:\w+\.)((?:\.?\w+)+)/); //first prop ignores 
      var path = matches[1]; 
      ret = new PropertyPath(path.split(".")); 
      this.paths[funcBody] = ret; 
     } 
     return ret; 
    }; 

    path : Array<string> 

    constructor(path : Array<string>) { 
     this.path = path 
    } 

    getValue(context : any) { 
     const me = this; 
     var v : any; 
     return this.path.reduce((previous, current, i, path) => { 
      try { 
       return previous[current]; 
      } 
      catch (e) { 
       throw { 
        message : `Error getting value by path. Path: '${path.join(".")}'. Token: '${current}'(${i})`, 
        innerException: e 
       }; 
      } 
     }, context) 
    } 

    setValue(context : any, value : any) { 
     const me = this; 
     var v : any; 
     this.path.reduce((previous, current, i, path) => { 
      try { 
       if (i == path.length - 1) { 
        previous[current] = value 
       } 
       return previous[current]; 
      } 
      catch (e) { 
       throw { 
        message : `Error setting value by path. Path: '${path.join(".")}'. Token: '${current}'(${i}). Value: ${value}`, 
        innerException: e 
       }; 
      } 
     }, context) 
    } 

} 

Пример использования:

var p = PropertyPath.get((data:Data) => data.person.middleName) 
var v = p.getValue(data) 
p.setValue(data, newValue) 

Некоторые сахара над ним:

export class PropertyPathContexted { 

    static get<T, P>(obj : T, lambda: (prop:T) => P) : PropertyPathContexted { 
     return new PropertyPathContexted(obj, PropertyPath.get(lambda)); 
    }; 

    context: any 
    propertyPath: PropertyPath 

    constructor(context: any, propertyPath: PropertyPath) { 
     this.context = context 
     this.propertyPath = propertyPath 
    } 

    getValue =() => this.propertyPath.getValue(this.context) 

    setValue = (value : any) => {this.propertyPath.setValue(this.context, value) } 

} 

И использование:

var p = PropertyPathContexted.get(data,() => data.person.middleName) 
var v = p.getValue() 
p.setValue("lala") 

Я нахожу последний очень удобно в двухсторонним в React привязки данных:

var valueLink = function<T, P>(context: T, lambda: (prop:T) => P) { 
    var p = PropertyPathContexted.get(context, lambda); 
    return { 
     value: p.getValue(), 
     requestChange: (newValue) => { 
      p.setValue(newValue); 
     } 
    } 
}; 

render() { 
    var data = getSomeData() 
    //... 
    return (
     //... 
     <input name='person.surnames' placeholder='Surnames' valueLink={valueLink(data,() => data.person.surnames)}/> 
     //... 
    ) 
} 
Смежные вопросы