-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
This function types #6739
This function types #6739
Conversation
Syntax is the same as a normal parameter: ```ts function f(this: void, x: number) { } ```
If `this` is not provided, it defaults to `void` for functions and `this` for methods. The rules for checking are similar to parameter checking, but there's still quite a bit of duplication for this implementation.
The new overloads use this types to specify the return type of these functions as well as the type of `thisArg`.
1. Display of `this` changes for quick info. 2. The type of Function.call/apply/bind is more precise.
@ahejlsberg and @DanielRosenwasser, I believe you were both interested in this. |
@@ -1297,6 +1297,9 @@ namespace ts { | |||
// as other properties in the object literal. So we use SymbolFlags.PropertyExcludes | |||
// so that it will conflict with any other object literal members with the same | |||
// name. | |||
if (options.strictThis) { | |||
seenThisKeyword = true; | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So any interface containing a method is now generic. Probably unavoidable, but we should get some data on what it means for performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ran some numbers this afternoon and didn't see a big change. Could be misreading the results though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to recap your findings: On the Monaco project this ends up adding as much as 10% to the check time for an overall impact of up to 5%.
(2) is on the last line of thisTypeInFunctions.ts |
Not that #7097 is fixed, can strictThisChecks be readded? |
@Gaelan I believe we concluded at our design meeting that we were going to hold off on |
@DanielRosenwasser I added a contextual typing test as in (2) with an implicit any on a this parameter. Note that the types are not quite right — the The effect is that this: o.someMethod = function(this, m) { return this.n + m }; has these types: o.someMethod: (this: O, m: number) => number // source of context
function(this: any, m: number) => number // this: any, but returns number
this: O // inside the body, this is contextually typed
this.n: number
m: number
return this.n + m // correctly returns number Note that the |
This feature is exactly what I'm after, I see it was merged but i can't find any docs regarding, and i saw here that it was not working well. |
https://www.typescriptlang.org/docs/handbook/functions.html has the documentation near the bottom of the page. |
Implements the proposal at #6018 and finishes the work described at #3694. See the bottom of this description for a tutorial and usage recommendations for this new feature.
This change adds checking for this types in functions. With this-function types, you can prevent the use of methods as free functions. And this types allow you to make constructor functions that return a real type rather than
any
. A number of other common javascript patterns can be given types or even inferred with--strictThis
turned on.Correctly prevent references to
this
when assigning callbacksNote that callback functions are prevented from referring to members of
this
because they declare thatthis: void
in order to be assignable toonClick
.Also note that the less common case of callback methods still allows functions to be assigned to these properties:
You can even require a different
this
for methods that will be used as a callback:Contextual typing of methods and functions in object literals
Now when an object literal declares that it is of some type,
this
is also contextually typed inside the object literal.Build object literals from existing functions
You can also define functions alone and then later build an object literal from them. If the functions specify the
this
type, then the compiler will check references to this in the function's body. And it will also check that the function'sthis
is assignable to thethis
of the object literal's type.Defining a function this way also requires that it cannot be called free:
The type of functions when used as constructors is now known.
When you use
new
with a function, you can now declare the type of the object that will be constructed withthis
. Previously, the type was alwaysany
. The compiler will also check that your assignments tothis
are correct in the body of the function.strictThisChecks
flag default typesUPDATE: --strictThisChecks removed
Because of performance concerns and lack of information on how people will use
this
types, we decided to remove--strictThisChecks
for this release. I left the original explanation below.--noImplicitThis
--noImplicitThis
makes it an error to usethis
that is implicitlyany
inside of a function:Add an annotation to fix this:
Previous explanation of
strictThisChecks
For backward compatibility,
this
types default toany
if not specified. However, if--strictThis
is specified, then functions will defaultthis: void
and methods will defaultthis: this
. This removes the need for most of the annotations in the previous examples. For example, an interface can declare a function withthis: void
by using function syntax and a method withthis: this
by using method syntax:This breaks a lot of existing code, but is easy to write for future code.
How to upgrade code to
--strictThis
When you switch on
--strictThis
you'll see a lot of errors with calling a method as if it were a function:You can fix the usage or the interface definition:
Designing new, strict-this code
To be able to switch
--strictThis
on, the style that you write interfaces needs to change. You need to consider how people will use your interfaces. If they will treat your interface's functions as methods on an object, you should use the normal method declaration syntax. If they will treat them as a callback or some other kind of free function, you should declare them using the function property syntax. For example:A safe default is to use the method declaration syntax. I discuss the tradeoffs below.
OO programming style
If you are writing pure OO Typescript/ES6, then you don't need to change much to work with
--strictThis
.this: this
this: void
.Fortunately, these are the default types for the method and function syntax, respectively, so you don't have to write anything much different:
Now you can't assign
extract
to a function by mistake:And if you implement an interface, your methods get the right this-type regardless of what syntax you use:
How can I use a method as a callback?
You may have noticed that
XmlExtractor.callback
isn't that useful as a method since you can't actually refer to any other methods. The solution is the same as you use today: wrap the method call inside a lambda:This formulation is OK because lambda (
=>
) doesn't bindthis
, sothis
comes from the class instead of from the implementing function.Functional style
If you are writing your code in functional style, you probably use interfaces to describe records of functions. You can still declare the functions using the method style and build instances of the interface using an object literal of your functions:
But this prevents users from pulling these functions off of
compiler
and using them individually:This is safe because of course they are free functions that do not require access to this. If you want enable this usage, you need to use the function property declaration syntax for the interface:
How can I implement an OO-style interface with nothing but functions?
If you want to implement an OO-style interface using only functions, you'll need to declare the
this
type in order to have access to that interface's members inside the function body.Then you can create an object literal that contains that function:
This is essentially the code you would write today, but now usages of
this
are checked. For even more convenience you can use the contextual typing that comes from writing a function inside an object literal:But at this point you might as well write a new class that implements Extractor.
Interfacing with JavaScript
The problem with interfacing to JavaScript is that it may use any or all of the above styles. A good default for writing types for Javascript code is the method declaration style. This makes implementing the interface easier. On the other hand, it prevents users of the interface from pulling functions off of the interface in order to save them as variables or to use them as callbacks.
This is the main reason that DefinitelyTyped definitions can't automatically compiled with --strictThis: it's impossible to predict which style of usage is the desired one for a currently-unmarked interface: