What are you trying to do? As @NitzanTomer points out you can't specialize generics like that in TypeScript, where Array<MyObject> has a method isEmpty(), but Array<string> does not. Depending on how you plan to use it, there are things that get you close.
Option 1: Extend Array prototype as you requested (not recommended)
The closest I can get to what you specifically asked for is to require the this parameter of isEmpty() to be of type Array<MyObject>:
declare global {
interface Array<T> {
isEmpty(this: Array<MyObject>): boolean;
}
}
// note the error below
Array.prototype.isEmpty = function() {
if (this.length == 0) {
return true;
} else {
return this.some((c) => c != null &&
c != '' // error! c has to be MyObject, not string
);
}
};
(As an aside, your implementation of isEmpty() has an error when you compare an array element, now required to be MyObject, to a string value. The good news is that TypeScript is alerting you of this so you can fix it.)
You can now go ahead and call isEmpty() on arrays of MyObject:
declare let myObject1: MyObject;
declare let myObject2: MyObject;
const arrayOfMyObject: MyObject[] = [myObject1, myObject2];
arrayOfMyObject.isEmpty(); // okay
Now, the isEmpty() method exists on all arrays, but trying to call it on an array of a different type will give you an error:
const arrayOfStrings: string[] = ['a', 'b'];
arrayOfStrings.isEmpty(); // error, string is not MyObject
So that might work for you.
That being said, extending native prototypes like Array is almost always considered a bad idea.
Option 2: Extend selected Array instances with new method
A better idea would be to create a brand new class with the extra method, or to take individual Array instances and add the method to them without polluting the Array prototype. Here's a way to do the latter:
interface MyObjectArray<T extends MyObject> extends Array<T>{
isEmpty(): boolean;
}
const isEmpty = function(this: Array<MyObject>) {
if (this.length == 0) {
return true;
} else {
return this.some((c) => c != null &&
c != '' // error! c is MyObject, not string
);
}
};
function toMyObjectArray<T extends MyObject>(arr: Array<T>): MyObjectArray<T> {
const ret = arr as MyObjectArray<T>;
ret.isEmpty = isEmpty.bind(ret);
return ret;
}
Now we have a new interface called MyObjectArray which includes the extra method. And if you have an existing instance of MyObject[], you can convert it to MyObjectArray using the toMyObjectArray() function, which adds the isEmpty() method to it. Then you can use it like this:
declare let myObject1: MyObject;
declare let myObject2: MyObject;
const arrayOfMyObject = [myObject1, myObject2];
arrayOfMyObject.isEmpty(); // error, no such method
const myObjectArray = toMyObjectArray(arrayOfMyObject); // convert
myObjectArray.isEmpty(); // okay
// can't convert an array of anything else
const arrayOfStrings = toMyObjectArray(['a', 'b', 'c']);
There's an extra step of calling a conversion function every time you need to use isEmpty(), which might make this less desirable for you.
Option 3: Use standalone isEmpty() function, not method
In fact, if you are not going to mess with Array.prototype, and you have to call a function on the array instance, you might as well just skip the extra interface and use a standalone isEmpty() instead of bothering with methods:
const isMyObjectArrayEmpty = function(arr: Array<MyObject>) {
if (arr.length == 0) {
return true;
} else {
return arr.some((c) => c != null &&
c != '' // error! c is MyObject, not string
);
}
};
declare let myObject1: MyObject;
declare let myObject2: MyObject;
const arrayOfMyObject = [myObject1, myObject2];
isMyObjectArrayEmpty(arrayOfMyObject); // okay
const arrayOfStrings = ['a', 'b', 'c'];
isMyObjectArrayEmpty(arrayOfStrings); // error
Those are the options as I see them.