Should we have this?
Closed this issue · 19 comments
related discussion: microsoft/TypeScript#3694
Cool. Looking over a few of their examples, they make a good case (.bind()
, .apply()
w/mixins). I'm open to it.
Want to write up a few example use cases?
It's not a rhetorical question. This one is out of my league.
I suppose it's missing, that's it.
Yes, we should. this
is like regular parameters an argument that is passed to a function call. We should be able to type it.
Things that should be considered:
-
Classes are in fact functions, and just as you can define function expressions and function declarations, the class syntax has two components: class expressions and class declarations
-
regular function call, e.g.
f(42)
-
regular method call, e.g.
obj.f(42)
-
type inference/analysis/check of
this
in a function expression due to the declaration context- top level (depends on environment: Browser, NodeJS, ...)
- inner function declaration
- arrow functions (lexical this)
- (static) method of (sub-) class
- property in object literal
- property assignment to existing object
Ps: I just quickly scaned the TypeScript discussion yet.
@maiermic In most cases, this
should not be typed by the function using this
, but instead, define some typed subset of this
. Because this
is determined by the call-site usage, it's impossible for the user of this
to accurately determine its full exact shape.
it's impossible for the user of
this
to accurately determine its full exact shape.
@ericelliott Who is the user of this
? The author of the function?
What he's saying is that, unless you have a way to declare the executing context, this
can be (re-)set to anything. So using this
would require to have its pendant: the context himself declared in the documentation and tightly coupled somehow to the function signature.
That's why it's only useful—without any prior context setting—when the function doesn't expect something in particular.
In a lot of languages this
is considered redundant.
I am closing this for now and moving it to the backlog: it's not essential for the MVP.
In a lot of languages
this
is considered redundant.
Any example of a language with type annotations where this
is not bound to the method? In statically typed languages like Java and C# this
is of the enclosing class or interface type.
I am gonna re-introduce the problem at hand for the ones dropping by.
The main use case that we need to cover is—as explained by @ericelliott before—being able to restrict the type of "the call-site".
Now here's my take on this (pun intended), I think it would make sense to place it just before the function signature. e.g. <here> signatureName(paramName: Type) => ReturnType
If we really want to make it obvious we could reuse something recognizable by any JS dev:
// assumes Example has been defined
// either
Example.signatureName(paramName: Type) => ReturnType
// or
Example[signatureName(paramName: Type) => ReturnType]
I don't particularly support this proposal. The priority for me is the position/order.
Which means that the introduction of a new operator would do just as well:
Example :: signatureName(paramName: Type) => ReturnType
The ::
syntax seems promising for this
, mirroring the ECMAScript This-Binding Syntax proposal:
// Iterable::first() => a
const first = function () {
const [a] = this;
return a;
};
first.call([1,2,3]); // 1
I forgot about that proposal but now that you remind me I did participate in tc39/proposal-bind-operator#24
I like your example, it's simple and it's reusing a reserved type that has already been defined, much clearer than mine.
The problem will be the error case. The way it will be handled is tool-dependent hence off-topic in the rtype specification.
- Which syntax(es) should be supported?
// A
Iterable::first() => a
// B
::Iterable.first() => a
I am leaning toward A. Both is also a valid option.
- Are spaces allowed around
::
?
If so, shouldn't it be recommended?
I think you're misunderstanding the second syntax. In the TC39 proposal, the ::map()
syntax means that the return value from the previous expression will be bound to this
inside map()
.
I'm not sure how we'd use that in Rtype.
obj::func(val) // is equivalent to: func.call(obj, val) ::obj.func(val) // is equivalent to: func.call(obj, val)
Are you telling me that https://babeljs.io/docs/plugins/transform-function-bind/#detail is erroneous?
OK, but not sure how that makes sense.
::obj.func(val)
// is equivalent to:
func.call(obj, val)
// is equivalent to:
obj.func(val)
You were right it was a typo:
::obj.func(val)
// is equivalent to:
obj.func.call(obj, val)