【问题标题】:Interface Contravariance in TypeScriptTypeScript 中的接口逆变
【发布时间】:2014-01-27 13:50:19
【问题描述】:

作为一项智力练习,我想我会看看我可以在 TypeScript (0.9.5) 中实现一些 .Net 泛型成员有多近,我已经达到了List<T>,但不确定我能否取得进步.

(我意识到有一些解决方法,但我特别尝试使用与 .Net 库中存在的相同实现,主要是尝试了解 TypeScript 中仍然存在的限制)。

无论如何,忽略我似乎无法以任何有意义的方式重载我的构造函数的事实,在 .Net 源代码中,构造函数 List(IEnumerable<T> collection) 检查传递的 Enumerable 不为空,然后逆变转换它使用ICollection<T> c = collection as ICollection<T> 到ICollection。

在 TypeScript 中我这样做 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;

                    }
                }
            }
        }
    }
}

有没有其他人尝试过接口的协方差?如果有,你是怎么过的?

谢谢,

【问题讨论】:

  • 可能是演员:var c: ICollection&lt;T&gt; = &lt;ICollection&lt;T&gt;&gt;collection ;
  • 感谢 WiredPrairie,直接投射似乎是有效的!
  • Plug : @TomTregenna 我确实有一个用于 TypeScript 中常见数据结构的库:github.com/basarat/typescript-collections
  • 是的,谢谢 basarat,在我开始之前,我确实看到了您的出色工作。

标签: c# javascript .net typescript


【解决方案1】:

您可以使用 TypeScript 中的类型断言来抑制警告:

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

类型断言实际上并不强制转换值 - 在运行时这些类型注释或断言都不存在,因为它们都被删除了。运行时值完全不受类型断言的影响。

但这并不能解决您没有add 方法的问题。

另外,null 不太可能用于您的测试:

if (collection == null) {

null 通常只用于故意不存在的值,例如,您更有可能遇到undefined。您可以使用简写来测试这两个:

if (!collection) {

我知道您正在执行一项智力活动,因此下一条评论绝对不适用 - 但对于其他任何人来说,值得注意的是重新创建 List 和 Collection 等方法来复制 .NET 没有抓住重点TypeScript(和 JavaScript)是一种不同的语言。 TypeScript 程序中的所有数组都是通用的,因此以所有这些不同的方式包装它们几乎没有什么好处——这一切都变成了开销。但正如我所提到的,作为一项智力练习,这很有趣。

【讨论】:

  • 参数在编译时被检查。如果你传了1,那肯定和参数的类型注解不兼容,也就是IEnumerable&lt;T&gt;:constructor(collection?: IEnumerable&lt;T&gt;, capacity?: number) {
  • 我现在知道我测试了什么——我的界面是空的——所以没有什么要检查的。 :)
  • 哈!我也做过一次。结构类型确实有一些需要注意的事情:)
  • 谢谢大家,尝试逐字复制框架是一个有趣的练习,但最终我认为我所做的只是找到了一个非常好的反模式!你的 undefined vs null 对我来说确实是一个愚蠢的错误。当我到达 Array 对象时,我决定认输。 .Net 的实现中发生了很多事情,以至于我觉得我真的在做 JavaScript,即使尝试也是不公平的。再次感谢,如果您收到大量关于我编辑和删除帖子等的通知,我深表歉意。我仍然习惯于 Stack Overflow 界面。
  • 谓词collection == null 检查collectionnull 还是undefined。相比之下,!collection 也匹配 ""0。前者是首选。
【解决方案2】:

虽然 TypeScript 不直接支持协方差,但您可以在此实例中使用类型断言(恰好看起来像 C# 等其他语言中的强制转换):

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

TypeScript 没有简单的方法来确认类型断言操作是否正确(例如 collection 对象实际上是 ICollection&lt;T&gt;),因此您需要确定这对您是否重要。

而且,如果您希望 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;
    }
}

TypeScript 编译器将对如下所示的值生成检查:

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

【讨论】:

    猜你喜欢
    • 2023-02-21
    • 1970-01-01
    • 2021-09-18
    相关资源
    最近更新 更多