I had mixed feelings about using, but I’m into it now. I like seeing familiar conventions from other languages I’ve come to love, and it seems like the implementation will likely deliver the same QOL benefits I’m familiar with.
On that note, I know it’s contentious, but I’d love to see pattern matching baked in like we have it in Rust (or similar). I’ve come to love ts-pattern so much, and I’m constantly wishing it was part of TypeScript/JavaScript.
This also provides lots of nice ways to understand what that cleanup behavior was to the caller. We could return an array of the original function return value and the result of the cleanup function. Maybe this reveals a potential new monadic structure that would be useful...
I agree doing this in OO, like what's shown in the page's examples, without the execution environment's support is tricky... I'd rather see though a new class to handle this rather than adding this behavior by adding a special method - it seems to put us back in the direction of having lifecycle methods like what React supports in class components that the community has totally moved away from.
I think I don't like that triggering the `dispose` method only takes place when the keyword `using` is present. It'd be much cleaner that it is always invoked on any object whose extends `Disposable`. Unless I have that behavior wrong...
Arguments to the doSomeWork can be carried through to someCleanupFn as can any return values from doSomeWork.
If doSomeWork returns all the pieces that need to be cleaned up by someCleanupFn (or those pieces are part of the original arguments), then those values can also be passed in as arguments to someCleanupFn.
This pattern (passing original arguments through, along with return values, to a supporting function) hints at a more elegant solution as well - a new monadic structure that supports this behavior by default.
This [Symbol.dispose]() stuff is not intuitive to me. What are the brackets for? Is this an object property? Seems like a bad analogue to Swift’s defer and deinit keywords/methods.
Are you familiar with [Symbol.iterator]()? It's doing the same thing.
{[someValue]: ...} is a pattern used when initializing computed keys on objects [1]. Some people might not encounter it often, though.
Symbol.* is sometimes used to implement language-level methods, like Symbol.dispose here or Symbol.iterator in my example below [2]. JS knows what those are for, and has well-defined and documented ways of using them.
I think — and I'm willing to be wrong — it's a pretty useful convention once it clicks, and while it might be confusing at first, I've come to like it. In the iterator example, you can see how you can create a dynamic data structure from a POJO (generating 52 cards from only the suits and ranks of a deck, and being able to treat it like an array). It's a useful way to encapsulate data and logic about a collection of data. When combined with generators, this pattern is extremely powerful. But again, this is why some people might not encounter it often.
JavaScript allows you to access and define object properties in several ways. The key thing to know here is that obj.foo, obj[“foo”], and obj[myProp] are all equivalent, where foo is a property of obj, and myProp is a variable containing the string “foo”.
This behavior is extended into object and class definitions, too. {[myProp]: “value”} creates an object whose property is the value of myProp, while {myProp: “value”} is an object with the property “myProp”. Because of the special nature of Symbols, the bracket notation is used when creating an object or class whose properties include a Symbol.
On that note, I know it’s contentious, but I’d love to see pattern matching baked in like we have it in Rust (or similar). I’ve come to love ts-pattern so much, and I’m constantly wishing it was part of TypeScript/JavaScript.