2014-01-27 2 views
4

Как интеллектуальное упражнение, я подумал, что посмотрю, как я мог бы реализовать некоторые из .Net-генераторов в TypeScript (0.9.5), я дошел до List<T>, но не уверен, что я может прогрессировать.Контравариантность интерфейса в TypeScript

(Я понимаю, что для этого есть обходные пути, но я специально пытаюсь использовать ту же реализацию, что и в .Net-библиотеках, в основном, чтобы попытаться понять ограничения, все еще присутствующие в TypeScript).

В любом случае, игнорируя тот факт, что я не могу перегрузить конструкторы каким-либо значимым образом, в источнике .Net конструктор List(IEnumerable<T> collection) проверяет, что переданный Enumerable не является нулевым, а затем контравариантно передает его в ICollection с использованием ICollection<T> c = collection as ICollection<T>.

В машинописном я это делаю var c: ICollection<T> = collection (коллекция является IEnumerable<T>), но получить следующую ошибку:

Cannot convert 'IEnumerable<T>' to 'ICollection<T>': Type 'IEnumerable<T>' is missing property 'Add' from type 'ICollection<T>'.

Мой текущий код выглядит следующим образом:

export module System { 

    export module Collections { 

     export interface IEnumerator { 
      MoveNext(): boolean; 
      Reset(): void; 
      Current(): any; 
     } 
     export interface IEnumerable { 
      GetEnumerator(): IEnumerator; 
     } 
     export interface ICollection extends IEnumerable { 
      CopyTo(array: any[], index: number): void; 
      Count(): number; 
      SyncRoot(): any; 
      IsSynchronized(): boolean; 
     } 
     export interface IList extends ICollection { 
      [index: number]: any; 
      Add(value: any): number; 
      Contains(value: any): boolean; 
      Clear(): void; 
      IsReadOnly: boolean; 
      IsFixedSize: boolean; 
      IndexOf(value: any): number; 
      Insert(index: number, value: any): void; 
      Remove(value: any): void; 
      RemoveAt(index: number): void; 
     } 

     export module Generic { 

      export interface IEnumerator<T> extends System.Collections.IEnumerator { 
       Current(): T; 
      } 
      export interface IEnumerable<T> extends System.Collections.IEnumerable { 
       GetEnumerator(): IEnumerator<T>; 
      } 
      export interface ICollection<T> extends IEnumerable<T> { 
       Add(item: T): void; 
       Clear(): void; 
       Contains(item: T): boolean; 
       CopyTo(array: T[], arrayIndex: number): void; 
       Remove(item: T): boolean; 
       Count(): number; 
       IsReadOnly(); boolean; 
      } 
      export interface IList<T> extends ICollection<T> { 
       IndexOf(item: T): number; 
       Insert(index: number, item: T): void; 
       RemoveAt(index: number): void; 
       [index: number]: T; 
      } 
      export interface IReadOnlyCollection<T> extends IEnumerable<T> { 
       Count(): number; 
      } 
      export interface IReadOnlyList<T> extends IReadOnlyCollection<T> { 
       [index: number]: T; 
      } 

      export class List<T> implements IList<T>, System.Collections.IList, IReadOnlyList<T> { 
       private _defaultCapacity: number = 4; 

       private _items: T[]; 
       private _size: number; 
       private _version: number; 
       private _syncRoot: any; 

       constructor(collection?: IEnumerable<T>, capacity?: number) { 
        // NOTE: Capacity will be ignored is Collection is not null 
        //  This is because we don't appear to be able to overload ctors in TypeScript yet! 
        if (collection == null) { 
         if (capacity == null) { 
          this._items = new Array<T>(0); 
         } 
         else { 
          this._items = new Array<T>(capacity); 
         } 
        } else { 
         var c: ICollection<T> = collection; 

        } 
       } 
      } 
     } 
    } 
} 

Кто-нибудь еще пытался со/противопоказаны дисперсия с интерфейсами? Если да, то как вы это сделали?

Спасибо,

+1

Может быть, актерский состав: 'var c: ICollection = > коллекция;' – WiredPrairie

+0

Спасибо WiredPrairie, сделав прямое действие, действительно действует! –

+0

Plug: @TomTregenna У меня есть библиотека для общих структур данных в TypeScript: https://github.com/basarat/typescript-collections – basarat

ответ

2

Хотя машинопись не поддерживает напрямую со/противопоказанием дисперсии, вы можете просто использовать тип утверждение в этом случае (что случается выглядеть гипс на других языках, такие как C#):

var c: ICollection<T> = <ICollection<T>>collection; 

У TypeScript нет простого способа подтвердить правильность операции утверждения типа (например, объект collection фактически является ICollection<T>), поэтому вам нужно решить, важно ли это для вас.

И, если вы хотите another идеи упростить код немного, вы можете использовать значение по умолчанию для capacity параметра:

constructor(collection?: IEnumerable<T>, capacity: Number = 0) { 
    if (!collection) { 
     this._items = new Array<T>(capacity); 
    } else { 
     var c: ICollection<T> = <ICollection<T>> collection; 
    } 
} 

машинописи компилятор будет генерировать проверку на значение, которое выглядит следующим образом :

if (typeof capacity === "undefined") { capacity = 0; } 
5

Вы можете подавить предупреждение, используя утверждение типа в машинописном:

var c: ICollection<T> = <ICollection<T>> collection; 

Утверждения типа фактически не передают значение - во время выполнения ни один из этих типов аннотаций или утверждений не существует, поскольку все они удалены. Значение времени выполнения полностью не зависит от утверждения типа.

Но это не решит проблему, что у вас не будет метода add.

Кроме того, null маловероятное значение для теста здесь:

if (collection == null) { 

null обычно используется только для заведомо отсутствующих значений, вы гораздо больше шансов столкнуться с undefined, например.Вы можете проверить оба из них с помощью короткую руки:

if (!collection) { 

Я знаю, что вы выполняете интеллектуальную деятельность, так что следующий комментарий абсолютно не относится - но для кого-то еще стоит отметить, что воссоздающие вещи как List and Collection метод-for-method для репликации .NET пропускает то, что TypeScript (и JavaScript) - это другой язык. Все массивы в вашей программе TypeScript являются общими, поэтому вы получаете небольшую выгоду от их обертывания всеми этими разными способами - все это становится накладным. Но, как я уже упоминал, это интеллектуальное упражнение - это интересный материал.

+1

Аргументы * * проверены во время компиляции. Если вы передали '1', это определенно не совместимо с аннотацией типа параметра, которая является' IEnumerable ':' constructor (collection ?: IEnumerable , capacity ?: number) {' – Fenton

+0

Теперь я знаю, что я тестировал - мой интерфейс был пуст, поэтому проверить нечего. :) – WiredPrairie

+0

Ха! Я тоже это сделал. У структурной типизации есть кое-что, о чем нужно помнить :) – Fenton

Смежные вопросы