tracked-tools / tracked-built-ins

Tracked versions of JavaScript's built-in classes

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using `new TrackedArray<string>()` in TypeScript

richard-viney opened this issue · comments

Hi,

Is it expected to be able to write new TrackedArray<string>() in TypeScript?

I'm seeing the following:

// This works fine. The type of foo is string[].
const foo = TrackedArray.of<string>()
foo.push("item")

// This gives the error: "Property 'push' does not exist on type 'TrackedArray<string>'. ts(2339)".
// The type of bar is `TrackedArray<string>`
const bar = new TrackedArray<string>()
bar.push("item")

Should TrackedArray<T> be extending Array<T> in the type definitions?

Thanks.

hmm, I'm not sure it should, because we don't want to deopt the array constructor unintentionally by forcing an unrelated object to use its constructor (it'll have a different shape, so it could cause the array constructor, which is pre-JITed, to deopt when it sees its not a standard array).

It may be that we need to define TrackedArray as an interface and use some tricks to make TS think the class matches the interface. @dfreeman had a similar suggestion for TrackedObject: playground

The following seems to work for me:

declare module "tracked-built-ins" {
  interface TrackedArray<T> extends Array<T> {}

  export const TrackedArray: new <T>(array?: T[]) => TrackedArray<T>
}

However, I'm not certain how to get from() and of() in there as well. For my use case this doesn't matter though.

If I use the extended tracked wrapper in this package and do const myArray = tracked([]) then I have the same typing problems because that function returns a TrackedArray<T> which has essentially no methods on it. If it returned T[] I think it would work, should the return types here in decorator.ts be changed?

Actually the following is sufficient (I'm currently only using TrackedArray from this package):

declare module "tracked-built-ins" {
  export class TrackedArray<T> extends Array<T> {}
}

I understand that the library doesn't want to do class TrackedArray extends Array {...} internally for performance reasons, but as far as typings go the above seems potentially simplest?

Ended up adding the following to prevent a subtle error I encountered while using this library. Any thoughts on this?

declare module "tracked-built-ins" {
  export class TrackedArray<T> extends Array<T> {
    /**
     * This member protects against implicitly converting a variable of type
     * `Array<T>` to a variable of type `TrackedArray<T>` which would silently
     * lose autotracking. It prevents errors like the following:
     *
     *   class Foo {
     *     private names = new TrackedArray<string>();
     *
     *     setNames(newNames: string[]) {
     *       this.names = newNames;
     *     }
     *   }
     */
    private readonly _trackedBuiltIn: never
  }
}

I believe this was resolved by the changes made in #56 and #161 and released in 2.0. Can you confirm that that resolves the issues you were having, @richard-viney?

Yep all seems to be sorted in 2.0. Thanks!