13

Is it possible to create Kotlin like scope functions in Javascript/Typescript? Is there any library that does it?

Reference: https://kotlinlang.org/docs/reference/scope-functions.html

2
  • 2
    If you have any specific use cases that you're trying to solve that you would solve with Kotlin scope functions, it may be useful to post questions about them specifically (separately from the above), as there's probably a different but equally-effective way to deal with that use case in TypeScript/JavaScript. Sometimes, high-level constructs don't translate across languages. Commented Jan 20, 2021 at 11:18
  • 1
    some.expression(value).which?.couldBe?.null().let(someMethodToBeCalledWithExpressionAsParamIfNotNull) Commented Oct 6, 2021 at 16:55

3 Answers 3

8

No, you can't do that in JavaScript or TypeScript. But depending on why you're trying to do it, destructuring can help.

The closest you can get is to use the deprecated with statement (I don't recommend it), which adds an object to the top of the scope chain, so any freestanding identifier references are checked against the object's properties:

function example(o) {
    with (o) { // deprecated
        console.log(answer);
    }
}
const obj = {
    answer: 42
};
example(obj); // Outputs 42

There are several problems with with, though, which is why it's disallowed in the strict variant of JavaScript (which is the default inside modules, class constructs, and other new scopes created in ES2015+, as well as any function or script with "use strict"; at the beginning). Those same problems are why it's not supported in TypeScript (TypeScript will copy it over to the transpiled JavaScript code, but with an error, and it will use any as the type of symbols inside the with; example).

Another close version is to pass an object to a function that uses destructuring in its parameter list:

function example({answer}) {
    console.log(answer);
}
const obj = {
    answer: 42
};
example(obj); // Outputs 42

but a major caveat there is that you can't assign new values to properties that way (and worse, if you try — for instance, with answer = 67 — it updates the parameter's value but not the object's property value).

To deal with that, you might use destructuring inside the function instead, with const so you don't forget you can't update the value (or to get an early error if you try):

function example(o) {
    const {answer} = o;
    console.log(answer);
    // answer = 67;   // <== Would cause error
    // o.answer = 67; // <== Would work
}
const obj = {
    answer: 42
};
example(obj); // Outputs 42

Sign up to request clarification or add additional context in comments.

4 Comments

To add to this, depending on what the author's use cases are, prototype functions can be used to replicate some of the effects of Kotlin scope functions. E.g., Object.prototype.let = function (fn) { return fn(this); }. The scope chain itself is not altered (unlike with with), but they can allow for a more functional programming style. Of course, whether this is useful depends again on the desired use cases. scope-extensions-js is a small TS library which uses this technique.
Thanks @Mike. Just a small note: If you're doing to extend Object.prototype, it's important not to do it that particular way, because it creates an enumerable property on Object.prototype which will mess with poorly-thought-out for-in loops. Instead, use Object.defineProperty(Object.prototype, "let", { value(fn) { return fn(this); }, configurable: true, writable: true }); so the property is non-enumerable. (And all the caveats about extending native prototypes apply. E.g., never do it in a library, but in your own page/app, it can be okay.) :-)
Note that with is not supported in TypeScript: stackoverflow.com/questions/21067477/…
@Vadzim - Yeah, with is deprecated largely because you can't look at the code and reason about what's what (static analysis) -- which is what TypeScript kinda wants to do. :-D
8

If anyone is still looking for an answer, there is an excellent library for those scope functions for javascript (I am not affiliated):

https://github.com/TheDavidDelta/scope-extensions-js

1 Comment

The projects seems to be dead or on hiatus. You will probably want to use this fork: github.com/Starrah/scope-extensions-js
2

While not exactly what you're looking for, a pattern that I use let a lot for in Kotlin is

val thing = nullable?.let{ // case if not null } ?: run { // case if null }

The closest I've come to this in Javascript (well, typescript) in terms of readability and ergonomics is something like:

const thing = some?.deeply?.nested?.nullable?.thing ?? (() => {
// case if null
})

Which gets you halfway there. :)

1 Comment

Not quite, because in Kotlin, the lambda in your let accepts as an implicit it parameter a non-nullable version of the variable you applied let to. In JS, the lambda after the nullish-coalescing operator has no idea whether the variable is null, since it may have changed (the current value is not captured).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.