{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/3578","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/3578/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/3578/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/3578/events","html_url":"https://github.com/Microsoft/TypeScript/issues/3578","id":89677065,"number":3578,"title":"Proposal for Abstract Classes and Methods","user":{"login":"aozgaa","id":3226984,"avatar_url":"https://avatars.githubusercontent.com/u/3226984?v=3","gravatar_id":"","url":"https://api.github.com/users/aozgaa","html_url":"https://github.com/aozgaa","followers_url":"https://api.github.com/users/aozgaa/followers","following_url":"https://api.github.com/users/aozgaa/following{/other_user}","gists_url":"https://api.github.com/users/aozgaa/gists{/gist_id}","starred_url":"https://api.github.com/users/aozgaa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aozgaa/subscriptions","organizations_url":"https://api.github.com/users/aozgaa/orgs","repos_url":"https://api.github.com/users/aozgaa/repos","events_url":"https://api.github.com/users/aozgaa/events{/privacy}","received_events_url":"https://api.github.com/users/aozgaa/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/Committed","name":"Committed","color":"009800"},{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/Fixed","name":"Fixed","color":"bfe5bf"},{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/Suggestion","name":"Suggestion","color":"006b75"}],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":46,"created_at":"2015-06-19T22:51:15Z","updated_at":"2015-12-08T11:56:00Z","closed_at":"2015-07-01T23:16:46Z","body":"# Proposal for Keyword `abstract`\r\n\r\nThis is a proposal for a new keyword, `abstract`, to allow for incomplete/partially implemented classes.\r\n\r\n# Introduction\r\n\r\nThis proposal expands upon #6, #2946, #2947.\r\n\r\nConsider the following situation. A user would like to create a base class that shouldn't be instantiated as a template for derived classes. S/he might write something like\r\n\r\n```ts\r\nclass Animal {\r\n    public age : number;\r\n    public yearsLeft() { return 20 - this.age; }\r\n    public makeSound() : string { return \"???\"; }\r\n}\r\n\r\nclass Cow extends Animal {\r\n    makeSound() { return \"Moo\"; }\r\n}\r\n\r\nclass Cat extends Animal {\r\n    makeSound() { return \"Meow\"; }\r\n}\r\n```\r\n\r\nToday, the writer is forced to make a choice: either (i) Animal can be declared an interface, but then `yearsLeft` cannot have an implementation, or (ii) write the program as above, but then Animal can be instantiated and `makeSound` has a bogus implementation! Neither of these options is particularly attractive.\r\n\r\nWe propose `abstract` as a class declaration modifier to allow the programmer control over whether a class can be instantiated, and as a member function modifier to control whether said member function offers an implementation (and whether the enclosing class is abstract).\r\n\r\n# Details\r\n\r\nThe modifier `abstract` can prepend a class declaration or a member function declaration. Constructors cannot be declared abstract.\r\n\r\n* Objects whose type is `typeof C` where `C` is an `abstract`-declared class (hereafter abstract class) cannot be instantiated via calls to `new`. \r\n* A member function declared `abstract` does not have an implementation.\r\n* Abstract methods cannot be called within a class member function body via `super.foo()` where `foo` is an abstract method belonging to the parent class.\r\n* Abstract methods may be called within a class member function body via `this.foo()` where `foo` is an abstract method belonging to that class.\r\n* If a class has any abstract methods, then the class itself must be declared abstract. \r\n* Abstract methods are inherited. \r\n* Abstract methods can be overridden in the derived class, either by an abstract method (effectively a re-declaration) or a function declaration that offers an implementation. \r\n* A method cannot be simultaneously `abstract` and `static`.\r\n* A method cannot be simultaneously `abstract` and `private`.\r\n* If an abstract member declaration contains an explicit accessibility modifier, then the modifier must precede the `abstract` keyword.\r\n* Overloads on a member function must be either all abstract or all not abstract.\r\n* Overloads of abstract member functions must be declared adjacent to eachother.\r\n* Objects whose type is the type of an abstract class can only be assigned to other objects whose type is the type of an abstract class. For example, \r\n\r\nThe following code snippet summarizes valid and invalid usages of the `abstract` keyword\r\n```ts\r\n\r\nclass A {\r\n    // ...\r\n}\r\n\r\nabstract class B {\r\n    foo(): number { return bar(); }\r\n    abstract bar() : number;\r\n}\r\n\r\nnew B; // error\r\n\r\nimport myB = B;\r\nnew myB; // error\r\n\r\nvar BB: typeof B = B;\r\nvar AA: typeof A = BB; // error, AA is not of abstract type.\r\nnew AA;\r\n\r\nfunction constructB(Factory : typeof B) {\r\n    new Factory; // error -- Factory is of type typeof C\r\n}\r\n\r\nvar BB = B;\r\nnew BB; // error -- BB is of type typeof C\r\n\r\nvar x : any = C;\r\nnew x; // okay -- undefined behavior at runtime\r\n\r\n\r\nclass C extends B { } // error -- not declared abstract\r\n\r\nabstract class D extends B { } // okay\r\n\r\nclass E extends B { // okay -- implements abstract method\r\n    bar() { return 1; }\r\n\r\nabstract class F extends B {\r\n    abstract foo() : number;\r\n    bar() { return 2; }\r\n}\r\n\r\nabstract class F {\r\n    abstract qux(x : number) : string;\r\n    abstract qux() : number;\r\n    y : number;\r\n    abstract quz(x : number, y : string) : boolean; // error -- declarations must be adjacent\r\n    \r\n    abstract nom() boolean;\r\n    nom(x : number) boolean: // error -- use of modifier abstract must match on all overloads.\r\n}\r\n\r\nclass G { // error -- not declared abstract\r\n    abstract baz() : number;\r\n}\r\n```\r\n\r\n# Pros\r\n\r\n* Allows the programmer to write partial implementations of a class.\r\n* Provides a mechanism for the type system to warn about classes that shouldn't be instantiated.\r\n* `abstract` annotations clarify the role of base classes in various patterns.\r\n\r\n# Cons\r\n\r\n* Increases the complexity of the language.\r\n\r\n# To Be Discussed\r\n\r\n* Should abstract be allowed within class expressions?\r\n\r\n# Proposed Changes to Grammar\r\n\r\n## A.6 Classes\r\n\r\n&emsp;&emsp;*ClassDeclaration*:\r\n&emsp;&emsp;&emsp;abstract<sub>opt</sub> class *Identifier* *TypeParameters*<sub>opt</sub> *ClassHeritage* { *ClassBody* }\r\n\r\n&emsp;&emsp;*MemberFunctionDeclaration*:\r\n&emsp;&emsp;&emsp;*MemberFunctionOverloads*<sub>opt</sub> *MemberFunctionImplementation*\r\n&emsp;&emsp;&emsp;*AbstractMemberFunctionOverloads*\r\n\r\n&emsp;&emsp;*AbstractMemberFunctionOverloads*:\r\n&emsp;&emsp;&emsp;*AbstractMemberFunctionOverload*\r\n&emsp;&emsp;&emsp;*AbstractMemberFunctionOverloads* *AbstractMemberFunctionOverload*\r\n\r\n&emsp;&emsp;*AbstractMemberFunctionOverload*:\r\n&emsp;&emsp;&emsp;*AccessibilityModifier*<sub>opt</sub> abstract *PropertyName* *CallSignature* ;\r\n\r\n## A.10 Ambients\r\n\r\n&emsp;&emsp;*AmbientClassDeclaration*:\r\n&emsp;&emsp;&emsp;abstract<sub>opt</sub> class *Identifier* *TypeParameters*<sub>opt</sub> *ClassHeritage* { *AmbientClassBody* }\r\n\r\n&emsp;&emsp;*AbstractOrStatic*:\r\n&emsp;&emsp;&emsp;abstract\r\n&emsp;&emsp;&emsp;static\r\n\r\n&emsp;&emsp;*AmbientPropertyMemberDeclaration*:\r\n&emsp;&emsp;&emsp;*AccessibilityModifier*<sub>opt</sub> *AbstractOrStatic*<sub>opt</sub> *PropertyName* *CallSignature* ;\r\n\r\nEDIT: updated to clarify the assignability of objects of \"abstract type\"."},"comment":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/comments/162860697","html_url":"https://github.com/Microsoft/TypeScript/issues/3578#issuecomment-162860697","issue_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/3578","id":162860697,"user":{"login":"AJamesPhillips","id":526509,"avatar_url":"https://avatars.githubusercontent.com/u/526509?v=3","gravatar_id":"","url":"https://api.github.com/users/AJamesPhillips","html_url":"https://github.com/AJamesPhillips","followers_url":"https://api.github.com/users/AJamesPhillips/followers","following_url":"https://api.github.com/users/AJamesPhillips/following{/other_user}","gists_url":"https://api.github.com/users/AJamesPhillips/gists{/gist_id}","starred_url":"https://api.github.com/users/AJamesPhillips/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AJamesPhillips/subscriptions","organizations_url":"https://api.github.com/users/AJamesPhillips/orgs","repos_url":"https://api.github.com/users/AJamesPhillips/repos","events_url":"https://api.github.com/users/AJamesPhillips/events{/privacy}","received_events_url":"https://api.github.com/users/AJamesPhillips/received_events","type":"User","site_admin":false},"created_at":"2015-12-08T11:56:00Z","updated_at":"2015-12-08T11:56:00Z","body":"@aleksey-bykov agreed, I shouldn't have said mixins as practically they share major downsides with multiple inheritance.  For the moment I'm:\r\n\r\n* making sure my inheritance trees are as shallow as possible\r\n* using the is-a guideline\r\n* utility functions\r\n* And when all that fails because you have multiple base classes something \"must\" inherit from (and which utility functions can't deal with elegantly) then the constructor sets public and or private attributes set to helper functions and or class instances; with or without a reference to the instance.\r\n\r\nWould be very interested to see composition / [stamps](https://github.com/stampit-org/stampit) / aspect / traits, in TypeScript."}}