As an intellectual exercise, I thought I'd see how closely I could implement some of the .Net generics members in TypeScript (0.9.5), I've gotten as far as List<T>
but not sure I can progress.
(I realise there are workarounds for this, but I'm specifically trying to use the same implementation as exists in .Net libs, mostly to try and get an idea of the limitations still present in TypeScript).
Anyway, ignoring the fact that I don't seem to be able to overload my constructors in any meaningful way, in the .Net source the constructor List(IEnumerable<T> collection)
checks that the passed Enumerable is not null, and then contravariantly casts it to an ICollection using ICollection<T> c = collection as ICollection<T>
.
In TypeScript I do this var c: ICollection<T> = collection
(collection is an IEnumerable<T>
), but get the following Error:
Cannot convert 'IEnumerable<T>' to 'ICollection<T>': Type 'IEnumerable<T>' is missing property 'Add' from type 'ICollection<T>'
.
My current code is as follows:
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;
}
}
}
}
}
}
Has anyone else tried co/contra-variance with interfaces? If so, how did you get on?
Thanks,
You can suppress the warning using a type assertion in TypeScript:
var c: ICollection<T> = <ICollection<T>> collection;
Type assertions don't actually cast the value - at runtime none of these type annotations or assertions exist as they are all removed. The runtime value is entirely unaffected by a type assertion.
But this doesn't solve the problem that you won't have an add
method.
Also, null
is an unlikely value for your test here:
if (collection == null) {
null
is usually only used for deliberately absent values, you are much more likely to encounter undefined
, for example. You can test for both of these using the short hand:
if (!collection) {
I know that you are performing an intellectual activity, so this next comment absolutely doesn't apply - but for anyone else it is worth noting that recreating things like List and Collection method-for-method to replicate .NET misses the point that TypeScript (and JavaScript) is a different language. All arrays in your TypeScript program are generic, so you get little benefit from wrapping them in all these different ways - it all just becomes overhead. But as I mentioned, as an intellectual exercise this is interesting stuff.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With