Extending Array in Typescript breaks constructor - arrays

while playing around with typescript I ran into then following interesting behavior:
class ExtArray<U> extends Array<U> {
constructor(...args : U[]) {
super(...args);
}
public contains(element : U) : boolean {
var i = this.indexOf(element);
return i !== -1;
}
}
var test : ExtArray<string> = new ExtArray("a", "b", "c");
test.push("y");
console.log(test.length); // 1
console.log(test[0]); // y
console.log(test[1]); // undefined
console.log("Has a: " + test.contains("a")); // Has a: false
console.log("Has y: " + test.contains("y")); // Has y : true
I've added the output of the console.log statements as comments.
See this typescript playground for an executable example and the javascript code.
As you can see it seems as if the elements passed to the constructor are not added to the array.
The section about extending expression in Whats new in Typescript suggests that it should be possible to extend the native Array type like that in typescript 1.6.
Also I didn't find anything in the typescript language reference,
that explains this behavior.
Most of the other questions about extending Arrays I found here are at least one year old and usually talk about a pre-1.0 version of typescript and therefore suggest to set up the prototype chain directly.
I seriously don't see what is going wrong here and I'm starting to suspect a typescript bug.
Or at least some kind of undocumented restriction for extending Arrays.
What goes wrong here?

It's a little easier to understand what's going on if you JSON.stringify() your object:
var test : ExtArray<string> = new ExtArray("a", "b", "c");
test.push("y");
// outputs {"0":"y","length":1}
document.writeln(JSON.stringify(test));
If you instead new-up a native Array, the resulting object is quite a bit different:
var test : Array<string> = new Array("a", "b", "c");
test.push("y");
// outputs ["a","b","c","y"]
document.writeln(JSON.stringify(test));
I agree with you that the documentation seems to imply that the subclass's constructor should behave the way you're expecting. Even stranger, I seem to get inconsistent results when testing whether or not the subclass is an Array using the methods described here:
test.constructor === Array // false
test instanceof Array // true
Array.isArray(test) // false
I would suggest opening an issue on the TypeScript GitHub repository. Even if this is the expected behavior, the official documentation is misleading and should clarify what exactly is expected when native objects are subclassed.

Related

Get Class Name of Class Subclassing Array in TypeScript?

I have the following class (ported from JavaScript wherein this works [prior to adding the types]) within TypeScript:
class A extends Array {
private _foo: string;
constructor(settings: any) {
super();
this._foo = 'foo';
}
get Foo() {
return (this._foo);
}
set Foo(value) {
this._foo = value;
}
}
let test = new A({ });
test.push('1');
test.push(2);
test.push('3');
// outputs "A" in JavaScript
// outputs "Array" in TypeScript
console.log(test.constructor.name);
The issue is outlined at the bottom of the code snippet, I'm expecting to get the class name back from test.constructor.name of "A" but am instead getting "Array". It seems like in the specific case of subclassing Array objects, TypeScript blows away the constructor name. Is there a way to find the class name within TypeScript which doesn't rely upon .constructor.name?
This happens because you have es5 as a target in compilerOptions and Typescript polyfills class
Have a look at generated code for es5 vs ES2015.
If you don't need to support really old browsers you can safely use a higher target than es5
I've tested your code in the Playground here
And it correctly outputs "A". I suspect something else is going on, possibly with your build tools or config.
I would suggest trying to use just tsc to narrow down the problem. If tsc correctly outputs, you can then move on to any other plugins, or build steps

Kotlin: Type inference failed: Not enough information to infer parameter E in fun <E> <init>(): kotlin.collections.ArrayList<E>

I declared a variable like this:
var G: Array<MutableList<Int>> = Array(0) { ArrayList() }
Kotlin gives me the following error:
Kotlin: Type inference failed: Not enough information to infer parameter E in fun <E> <init>(): kotlin.collections.ArrayList<E> /* = java.util.ArrayList<E> */
Please specify it explicitly.
It means Kotlin can't infer the type for the ArrayList which should be Int. So I add Int explicitly for the ArrayList like following:
var G: Array<MutableList<Int>> = Array(0) { ArrayList<Int>() }
Kotlin says - Remove explicit types arguments
In this case, Kotlin is ambivalent about how to act.
So is it possible to write code without explicitly declaring the type of ArrayList?
As discussed here,
The way it works curretly is that whenver we encounter a collection in Kotlin, we load a Kotlin version of this class (e.g. kotlin.Collection) instead of a Java version (java.util.*). Using the type java.util.Collection leads to a warning from the Kotlin compiler, because Kotlin's type checker is designed to distinguish between read-only and mutable collections.
So you can try to use like this,
var G = arrayOf<MutableList<Int>>()
Moreover, here are some a good stuff to know for you.
Kotlin says - Remove explicit types arguments
Kotlin doesn't (you can see there's no warning in https://pl.kotl.in/7v1h5Yobu). It's probably the IDEA plugin which does. If you look at https://youtrack.jetbrains.com/issues/KT?q=Remove%20explicit%20types%20arguments, you can see there are quite a few false positives. It may be worth checking if yours is actually one of them and posting a new issue if it isn't.
var G = Array<MutableList<Int>>(0) { ArrayList() }
should work without warning from IDEA either.

I can't access the _groups property in angular/d3js

I have a problem accessing my "_groups" property with the following code:
function mouseDate(scale){
var g = d3.select("#group")._groups[0][0]
var x0 = scale.invert(d3.mouse(g)[0]);
console.log(x0);
}
Result of my console.log:
Selection {_groups: Array(1), _parents: Array(1)}
_groups: Array(1)
0: Array(1)
0: g#group
When I compile the code I have the following error :
D:/Documents/starter-propre-angular4/src/app/pages/graphe-page/graphe.page.ts (782,32): Property '_groups' does not exist on type 'Selection<BaseType, {}, HTMLElement, any>'.
So my question is: Is there a solution to get the information in "_groups" all year round knowing that I am evolving into TypeScript using d3js
The _groups property is a private member of a Selection object and should as such not be accessed directly. (Side note: it is common convention in JavaScript that any member starting with an underscore denotes a private member. See, e.g., "Underscore prefix for property and method names in JavaScript").
Since the property is considered private it is not part of the public interface and is therefore not included in the TypeScript type declaration for the d3-selection module. Hence, you get the compiler error you witnessed. Although this actually will work in pure JavaScript the TypeScript compiler did exactly what it is supposed to do—namely, prevent you from doing some unsafe stuff.
Looking at the code you posted, though, it becomes apparent that you are not interested in the _groups property itself but rather in _groups[0][0] which internally refers to the first node of the selection. Fortunately, the selection.node() method will return exactly that first element. You can do it like this:
function mouseDate(scale){
var g = d3.select("#group").node();
var x0 = scale.invert(d3.mouse(g)[0]);
console.log(x0);
}

Typescript Class.prototype.MyFunction makes errors but working

I can't explain why I get an error but code works. Is that compiler bug? (I use Visual Studio Code with Angular 2)
class A
{
fun(a: number)
{
return a+2;
}
}
A.prototype.F = function() { return "F here!"+this.fun(1); } // This makes error: The property 'F' does not exist on value of type 'A'
var a: A = new A();
console.log(a.F());
And bonus: This is not working at all! (no access to this.fun())
A.prototype.F2 = () => { return "F2 here!"+this.fun(1); } // ()=>{} is not working! cause _this is not defined!
...
console.log(a.F2());
Edit #1
As #seangwright said I need to use Module Augmentation but...
As far as it's working with simple example with my A class I can't make it work with Angular's ComponentFixture. This should solve my problem if I do this like in Typescript example:
declare module '#angular/core/testing' // I was trying without this line and with 'global' instead of '#angular/core/testing' but nothing helps
{
interface ComponentFixture<T>
{
TestOf(cssSelector: string): string;
}
}
But I still get an error:
'ComponentFixture' only refers to a type, but is being used as a value
here.'
at this point:
ComponentFixture.prototype.TextOf = function(cssSelector: string): string
{
...
}
There is even more errors, for example when I try to use it:
let fixture: ComponentFixture<EditableValueComponent>;
fixture = TestBed.createComponent(EditableValueComponent);
I got:
'ComponentFixture' is not assignable to type
'ComponentFixture'. Two different types with
this name exist, but they are unrelated. Property 'TestOf' is
missing in type 'ComponentFixture'
So again: Code works but has many compilation errors. Or maybe I'm missing something obvious?
I get the feeling you are a C# developer based on how you format your code.
Part 1
In Typescript once you declare your class, the type system expects it to have the properties (shape) you define and that's it.
The more of the type system you want to use, the less dynamic your objects will/can be.
That said, the reason your code runs (transpiles) correctly is because this is an error in the context of Typescript's structural type system, not Javascript's dynamic type system. So Typescript will tell you A doesn't have a property F at compile time, but Javascript doesn't care that it's added at runtime.
One solution is to merge the class with an interface
class A {
fun(a: number) {
return a + 2;
}
}
interface A {
F(): string;
}
A.prototype.F = function () { return "F here!" + this.fun(1); }
var a: A = new A();
console.log(a.F());
Another would be to temporarily abandon the type system
class A {
fun(a: number) {
return a + 2;
}
}
(A.prototype as any).F = function () { return "F here!" + this.fun(1); }
var a: A = new A();
console.log((a as any).F());
But that becomes verbose and prone to errors and loses the benefits that a type system brings.
You mention you are using Typescript with Angular 2. You could write in ES2015 if you wanted a more dynamic syntax. But then you will lose some of the benefits that Angular 2 gets from using Typescript (better tooling, smaller deployments).
Part 2
The reason your second example doesn't work at all has nothing to do with Typescript and everything to do with Scope (or execution context) in Javascript, specifically ES2015 arrow functions.
An arrow function does not create its own this context, so this has its original meaning from the enclosing context.
Unlike in your first example you are not using the traditional function declaration syntax and instead are using the () => {} arrow function syntax. With your first example
A.prototype.F = function() { return "F here!"+this.fun(1); }
this refers to whatever context F() is going to be executing in. Since you define it on the prototype of A it is going to be executing in the context of A. A has a .fun() method so this.fun() is going to be the same one defined in your class above.
With your second example, F2 is not going to be executing in the context of A despite being defined as a method of its prototype. The arrow function syntax is instead going to allow F2 to run in the context of the enclosing context which is the global window object unless you are running in strict mode in which case
in browsers it's no longer possible to reference the window object through this inside a strict mode function.
So this will be undefined and calling fun() on undefined is going to throw an error.
Try adding a console.log(this) to your F2 function.
A.prototype.F2 = () => { console.log(this); return "F2 here!"+this.fun(1); }
When you run the transpiled Javascript you will probably see Window logged out to the console, and then probably an error like Uncaught TypeError: _this.fun is not a function
Use the Typescript Playground to write some Typescript, see what the tooling tells you, what transpiled Javascript is created and then run it to see if your Javascript is correct.

How to serialize/unserialize an Array of Custom object in Kotlin?

In my Kotlin Android project, I made a FileItem class which extends Serializable
class FileItem(<parameters>) : Serializable, Comparable<FileItem> {
So I needed to Serialize instances of this class into a Bundle
val arguments:Bundle = Bundle()
arguments.putSerializable("folders", folders as Serializable)
where folders has been declared as :
folders:Array<FileItem> (method parameter)
The serialization code above compile without any warning. Meanwhile, the problem comes when I need to unserialize folders items :
val arguments: Bundle? = getArguments()
if (arguments != null){
foldersItems = arguments.getSerializable("folders") as Array<FileItem>
where foldersItems is declared as
var foldersItems: Array<FileItem>?
I get the following warning, that I can't manage to solve without suppress_warning annotation :
w: <Path to my class>: (78, 28): Unchecked cast: java.io.Serializable! to kotlin.Array<com.loloof64.android.chess_positions_archiver.main_file_explorer.FileItem>
This kind of code compiles in Java/Groovy without warning (folderItems is then a FileItem[]), so how can I modify the kotlin code for the compiler to be "satisfied" ?
I noticed in official Kotlin documentation that Kotlin Array does not extend Serializable and is not open for inheritance. Is it possible meanwhite to "add" it via a kind of extension method ?
In fact, the cast is not unchecked, the compiler's warning is misleading.
This happens because in Kotlin arrays are represented by generic class Array<T>, and the compiler treats it as usual generic class with type parameters erased at runtime.
But on JVM arrays have reified types, and when you cast something as Array<SomeType>, the generated bytecode really checks the type parameter to be SomeType as well as something being an Array<*>, which would only happen for any other generic class.
This example shows that the array cast is checked:
val a: Any = Array<Int>(1) { 0 }
val i = a as Array<Int>
val d = a as Array<Double> // gets checked and throws ClassCastException
The easiest solution is indeed to #Suppress("UNCHECKED_CAST"), because actually there should not be any warning.
I filed an issue describing the problem in Kotlin issue tracker.
The cast here is unchecked because the compiler here can't ensure the nullability of array's generic type parameter.
Consider the following example:
fun castAsArrayOfString(param: Any) = param as Array<String>
castAsArrayOfString(arrayOf("a")) // is Array<String>, all ok
castAsArrayOfString(arrayOf("a", null)) // is Array<String>, but contains null
So the compiler warns you about potential type safety problems this cast could introduce.

Resources