Skip to content

Overloads in Array.concat now handle ReadonlyArray #21462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 2, 2018

Conversation

sandersn
Copy link
Member

Previously it was union types, which is slower.

Previously it was union types, which is slower.
@sandersn sandersn requested a review from mhegazy January 29, 2018 22:38
@ahejlsberg
Copy link
Member

I'm not a fan of adding more overloads. Instead, I propose we introduce a new type InputArray<T> and use that in place of T[] | ReadonlyArray<T> in the original two overloads.

interface InputArray<T> {
    readonly length: number;
    readonly [n: number]: T;
    join(separator?: string): string;
}
/**
  * Combines two or more arrays.
  * @param items Additional items to add to the end of array1.
  */
concat(...items: InputArray<T>[]): T[];
/**
  * Combines two or more arrays.
  * @param items Additional items to add to the end of array1.
  */
concat(...items: (T | InputArray<T>)[]): T[];

This works because InputArray<T> omits the methods that cause ReadonlyArray<T> and Array<T> to become invariant in --strictFunctionTypes mode. I have kept the join method in InputArray<T> to ensure that it isn't compatible with the string type or other types that might look like arrays. Now, technically this is slightly less type-safe than using T[] | ReadonlyArray<T>, but not in any meaningful fashion.

The baselines look fine with this change and checker performance when compiling the compiler improves by as much as 10%!

@mhegazy
Copy link
Contributor

mhegazy commented Feb 1, 2018

sounds good to me.

@sandersn
Copy link
Member Author

sandersn commented Feb 1, 2018

@DanielRosenwasser and I considered this approach but didn't use it because it didn't show as much performance improvement as the current one.
However, I double-checked with the cleaned-up perf tests and confirmed that the performance improvement for the compiler is around 10%. It barely helped the other projects, except for TFS. This includes compiler-with-unions.

@sandersn
Copy link
Member Author

sandersn commented Feb 1, 2018

Performance is the same between the additional overloads and the additional interface. I would say it's down to which we think is easier to understand in quickinfo.

@sandersn
Copy link
Member Author

sandersn commented Feb 1, 2018

After some discussion with @weswigham, he points out that the additional interface is more dangerous because you can satisfy it with { length: 1, join(s) { return s } }, which will fail at runtime. Almost anything works; you just need a join(string): string method.

@ahejlsberg
Copy link
Member

@mhegazy @sandersn I looked at the original issue (#20268) in more depth and I'm now convinced that it isn't a bug. See this comment #20268 (comment).

@ahejlsberg
Copy link
Member

ahejlsberg commented Feb 2, 2018

Nevermind, #20268 is indeed a bug as I explain here #20268 (comment).

I think the best way to fix it is the InputArray<T> that omits indexOf and the other methods that cause invariant behavior. If we're concerned about false matches, we can add the slice method as well:

interface InputArray<T> {
    readonly length: number;
    readonly [n: number]: T;
    join(separator?: string): string;
    slice(start?: number, end?: number): T[];
}

I think it is extremely unlikely something will match by accident. And, even with the current definitions something could match by accident. That's the nature of structural typing.

@sandersn
Copy link
Member Author

sandersn commented Feb 2, 2018

I don't think making the interface one method larger buys us that much safety in practice. I'd prefer to leave InputArray as it is.

Should make it, respectively, easier to understand this specific type
and harder to satisfy it by mistake.
@sandersn sandersn merged commit be0fcd5 into master Feb 2, 2018
@sandersn sandersn deleted the use-overloads-for-concat branch February 2, 2018 21:20
sandersn added a commit that referenced this pull request Feb 2, 2018
* Overloads in Array.concat now handle ReadonlyArray

Previously it was union types, which is slower.

* Make arrayConcat3 test stricter

* Switch to InputArray instead of adding overloads

* Update baselines

* Update baselines correctly

* Rename to ConcatArray and add slice method

Should make it, respectively, easier to understand this specific type
and harder to satisfy it by mistake.
@sandersn
Copy link
Member Author

sandersn commented Feb 2, 2018

This is now in 2.7 as well.

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants