Now that augments / monkey patches are live in 1.8, maybe we could consider a move towards safer augments that are scoped to the file they're used in.
As a current workaround, I'm using explicit conversions, which are a Typescript take on implicit classes. (The difference being you have to explicitly invoke the conversion). They provide _scoped_ monkey-patching. Here is some sample code written in this style.
Would these be considered as a future addition to TypeScript with nicer syntax and direct compiler support?
Can you provide more details of the proposal?
I'm a big fan of Scala and while the ability to extend existing types in a non-prototype-polluting way would provide a very meaningful level of abstraction I'm not sure that implicit classes would be a good fit for TypeScript as they may not map well to JavaScript.
I assume that you are proposing something like this
implicit class ArrayWithGroupBy<T> {
constructor(private array: T[]) {}
groupBy<TKey>(keySelector: (value: T) => TKey): Map<TKey, T[]> {
return this.array.reduce((groups, value) => {
const key = keySelector(value);
if (!groups.has(key)) {
groups.set(key, [value]);
}
else {
groups.get(key).push(value);
}
return groups;
}, new Map<TKey, T[]>());
}
}
...
let colors = ["red", "ruby", "blue", "teal", "tan", "beige"];
// translation : new ArrayWithGroupBy(colors).groupBy(color => color[0]);
let byLength = colors.groupBy(color => color[0]);
console.info(byLength); // Map {"r" => ["red", "ruby"], "b" => ["blue", "beige"], "t" => ["teal", "tan"]}
This would be super useful, but it doesn't seem very JavaScript.
Still this is an interesting idea.
Edit: Fixed types; fixed output
A new language construct is proposed to create functions which provide extensions to another type. These are similar to augmentations, but are limited to their initial scope.
The implicit keyword will now be allowed on functions. Functions annotated with the implicit keyword are referred to as _implicit conversions_.
An _implicit conversion_ must have exactly one argument and must return an instance of an Object.
The _implicit conversion_ is only active in the scope where it's been declared or imported. _Implicit conversions_ do not carry over across files or through function invocations.
When an _implicit conversion_ is active, any function or property resolution errors will not immediately cause compilation to fail. The _implicit conversion_ will attempt to apply itself to the object failing compilation, returning a new object. This object is then re-tested for resolution, and that result is final.
To create an _implicit conversion_, place the implicit keyword in front of a function declaration and return an extended object from that function:
/* rich_string_implicits.ts */
class RichString extends String {
get capitalized(): string {
return this[0].toUpperCase() + this.slice(1);
}
}
implicit function RichStringImplicits(s: string): RichString {
return new RichString(s);
}
export default RichStringImplicits;
This example creates an _implicit conversion_ that wraps a String object and extends it with a new getter, capitalized. To use this conversion, import it into scope and call the capitalized getter. Here's an example:
/* person.ts */
import RichStringImplicits from './rich_string_implicits.ts';
class Person {
firstName: string
lastName: string
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
get fullName(): string {
return this.firstName.capitalized + this.lastName.capitalized;
}
}
The language specification (6.1) would be modified to allow the use of the implicit modifier for functions.
  _FunctionDeclaration: ( Modified )
   implicitopt function BindingIdentifieropt CallSignature { FunctionBody }
   implicitopt function BindingIdentifieropt CallSignature ;_
The new syntax should not break existing code.
Having several active _implicit conversions_ may slow down compilation in code with many errors.
_Implicit conversions_ and augmentations may need to merge to avoid syntax bloat.
This is a proposal for implicit conversions - implicit classes are a small layer of syntax sugar on top of this ^.
Bump! Any comments?
This looks like a library feature and not a compiler transformation. so i am inclined to say this is out of scope of the TS project. leaving the issue open for now to get more feedback.
I'm not sure this can be implemented as a library - what did you have in mind?
Bump! As of now, only interfaces/namespaces/modules can be patched (per merging rules).
A great use case for this is for mocking complex dependencies. Currently, there's no good way to monkey patch dependencies of dependencies reliably - but this solved by this proposal.
@aluanhaddad
I'd say monkey patching is a long-standing, but frowned upon, JavaScript tradition 😛
This approach lets us eliminate its dark side by reconciling and type-checking the monkey patches at compile-time, avoiding errors and performance gotchas.
There doesn't seem to be much demand for this; closing.
Most helpful comment
@aluanhaddad
I'd say monkey patching is a long-standing, but frowned upon, JavaScript tradition 😛
This approach lets us eliminate its dark side by reconciling and type-checking the monkey patches at compile-time, avoiding errors and performance gotchas.