{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4910","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4910/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4910/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4910/events","html_url":"https://github.com/Microsoft/TypeScript/pull/4910","id":107614930,"number":4910,"title":"Polymorphic 'this' type","user":{"login":"ahejlsberg","id":4226954,"avatar_url":"https://avatars.githubusercontent.com/u/4226954?v=3","gravatar_id":"","url":"https://api.github.com/users/ahejlsberg","html_url":"https://github.com/ahejlsberg","followers_url":"https://api.github.com/users/ahejlsberg/followers","following_url":"https://api.github.com/users/ahejlsberg/following{/other_user}","gists_url":"https://api.github.com/users/ahejlsberg/gists{/gist_id}","starred_url":"https://api.github.com/users/ahejlsberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ahejlsberg/subscriptions","organizations_url":"https://api.github.com/users/ahejlsberg/orgs","repos_url":"https://api.github.com/users/ahejlsberg/repos","events_url":"https://api.github.com/users/ahejlsberg/events{/privacy}","received_events_url":"https://api.github.com/users/ahejlsberg/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/cla-not-required","name":"cla-not-required","color":"009800"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":30,"created_at":"2015-09-21T23:00:11Z","updated_at":"2015-09-30T17:13:18Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/Microsoft/TypeScript/pulls/4910","html_url":"https://github.com/Microsoft/TypeScript/pull/4910","diff_url":"https://github.com/Microsoft/TypeScript/pull/4910.diff","patch_url":"https://github.com/Microsoft/TypeScript/pull/4910.patch"},"body":"This PR implements polymorphic typing of `this` for classes and interfaces as inspired by #3694. The new features are:\r\n\r\n* The type of `this` in an expression within a non-static class or interface member is considered to be an instance of *some class that derives from the containing class* as opposed to simply an instance of the containing class.\r\n* The `this` keyword can be used in a type position within a non-static class or interface member to reference the type of `this`.\r\n* When a class or interface is referenced as a type, all occurrences of the `this` type within the class (including those inherited from base classes) are replaced with the type itself.\r\n\r\nThis feature makes patterns such as [fluent interfaces](https://en.wikipedia.org/wiki/Fluent_interface) and [covariant return types](https://en.wikipedia.org/wiki/Covariant_return_type) much easier to express and implement. Languages such as C++, Java, and C# use a somewhat involved generic pattern to emulate this feature, [as described here](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).\r\n\r\nAn example:\r\n\r\n```typescript\r\nclass A {\r\n    foo() {\r\n        return this;\r\n    }\r\n}\r\n\r\nclass B extends A {\r\n    bar() {\r\n        return this;\r\n    }\r\n}\r\n\r\nvar b: B;\r\nvar x = b.foo().bar();  // Fluent pattern works, type of x is B\r\n```\r\n\r\nIn a non-static member of a class or interface, `this` in a type position refers to the type of `this`. For example:\r\n\r\n```typescript\r\nclass Entity {\r\n    clone(): this {\r\n        // Code to clone Entity\r\n    }\r\n    equals(other: this): boolean {\r\n        // Code to compare Entity with another Entity\r\n    }\r\n}\r\n```\r\n\r\nEach subclass of the above `Entity` will have a `clone` method that returns an instance of the subclass, and an `equals` method that takes another instance of the subclass.\r\n\r\nThe `this` type is a subtype of and assignable to the instance type of the containing class or interface, *but not vice-versa* (because `this` might actually be a subclass). Thus, certain code patterns that previously compiled may now need an extra type annotation:\r\n\r\n```typescript\r\nclass A {\r\n    getInstance() {  // Should be getInstance(): A\r\n        return this;\r\n    }\r\n}\r\n\r\nclass B extends A {\r\n    getInstance() {\r\n        return new B();\r\n    }\r\n}\r\n```\r\n\r\nThe example above now errors because the inferred return type of `getInstance` in `A` is `this` and the inferred type of `getInstance` in `B` is `B`, which is not assignable to `this`. The fix is to add a return type annotation for `getInstance` in `A`.\r\n\r\nThe polymorphic `this` type is implemented by providing every class and interface with an implied type parameter that is constrained to the containing type itself (except when the compiler can can tell that `this` is never referenced within the class or interface). That in particular turns a lot of previously non-generic classes into generic equivalents, causing more symbols and types to be created due to generic instantiation. The observed cost in batch compile time ranges from 0% in code that uses no classes to 6-7% in very class-heavy code.\r\n\r\nNote that this PR specifically doesn't aim to implement other parts of #3694 such as `this` type annotations for functions. Those will be covered by other PRs if we choose to implement them."},"comment":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/comments/144480131","html_url":"https://github.com/Microsoft/TypeScript/pull/4910#issuecomment-144480131","issue_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/4910","id":144480131,"user":{"login":"mhegazy","id":8000722,"avatar_url":"https://avatars.githubusercontent.com/u/8000722?v=3","gravatar_id":"","url":"https://api.github.com/users/mhegazy","html_url":"https://github.com/mhegazy","followers_url":"https://api.github.com/users/mhegazy/followers","following_url":"https://api.github.com/users/mhegazy/following{/other_user}","gists_url":"https://api.github.com/users/mhegazy/gists{/gist_id}","starred_url":"https://api.github.com/users/mhegazy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhegazy/subscriptions","organizations_url":"https://api.github.com/users/mhegazy/orgs","repos_url":"https://api.github.com/users/mhegazy/repos","events_url":"https://api.github.com/users/mhegazy/events{/privacy}","received_events_url":"https://api.github.com/users/mhegazy/received_events","type":"User","site_admin":false},"created_at":"2015-09-30T17:13:18Z","updated_at":"2015-09-30T17:13:18Z","body":":+1: "}}