Closed disshishkov closed 7 years ago
+1
+1
+1 for code generation. I have been getting into angularjs and webapi and I want to make a tool that essentially creates automatic JS definitions from my c# objects and services. I want to then be able to extend these classes without having to edit the "scaffolded" JS definitions. I've see that a few other people are requesting this and it seems like we have similar use cases.
+1 for code generation
+1 for code generation, angular has lots of boilerplate code, we're already doing lots of generation of it, but with partial classes we would be able to do much more.
+1 yet again for code generation. Trying to extend classes in another file is messy at best.
@RyanCavanaugh, what is needed to get this out of "+1" mode and moving in a productive direction?
-1 a following syntax is better for mixins: #2919
Not sure why can't we have both partial classes and the mixins; two totally unrelated features.
+1
This would be nice when code is generated, but you don't want to modify it to add things to the generated code.
+1
I have a proposal that should satisfy both of these use cases:
For (2), normally you would just use the ::
operator to get infix syntax.
But if you want an old class to be part of a new interface, or if you want to dynamically dispatch, you will need to actually modify the prototype.
interface ITimes {
times(n: number): number
}
Array implements ITimes {
times(n: number): number {
return this.length * n
}
}
// The compiler should check that all methods of ITimes and IOther are implemented.
Number implements ITimes, IOther {
times(n: number): number {
return this * n
}
other(): void {}
}
// The old types should typecheck with the new interface.
const x: ITimes = true ? [] : 0
x.times(3)
// The interface list can be empty. This essentially gives you partial classes.
MyClass implements {
anotherMethod(): void {}
}
(In the example I use string method names, but since these are builtin types, using symbols would be better once those can be typechecked.)
An improvement that this has over true partial classes is that the class has a single definition, and other places merely extend it. This means there's a clear place to import the class from and makes translation to JS easier.
:+1: I would love to see partial classes too.
What is the status of this feature?
This use case for example :
I have a Math
package that define a class Set
, with methods Set#add
, 'Set#remove'
// math.ts
export partial class Set {
add(){}
remove(){}
}
I have an optional, heavy, Relational
package that define classes Relation
and Tuple
.
This Relational
package would also add a Set#product
method.
// relational.ts
///<reference path="./../math/tsd.d.ts"/>
partial class Set {
product(){}
}
export class Relation(){}
export class Tuple(){}
Partial classes would allow me to compose classes in a more flexible way. If I don't want to use the heavy relational
package, I can still use the basic Set functionality. Other partial
classes will only come in and add extra functionality to the class.
I'm scratching my head to do something that will have a nice API without the use of partials in this case.
All I could come with is some repository pattern.
// Without partials
// base
export class Base {
static registerOperation(opName, method){
this.prototype[opName] = method;
}
operation(name, ...args){
return this[name].apply(this, args);
}
}
// math.ts
import {Base} from './base';
class Set extends Base{
// Define the methods here
add(){}
remove(){}
}
// Or here
Set.registerOperation("add", function(...){});
Set.registerOperation("remove", function(...){});
// relational.ts
import {Set} from './../math/math';
Set.registerOperation("product", function(...){});
// app.ts
import {Set} from 'math';
var set = new Set();
set.add // compiler error
set.remove // compiler error
set.product // compiler error
// have to use something like
set.operation("add", args);
// or
(<any>set).add(arg);
+1 I'm using t4 to generate classes and proxies over WebApi. I think auto-generation is using vastly in typescript and when it comes to auto-generation, partial classes is necessary!
It would be great for splitting aspects of the same class especially in React +1
+1 Code generation and "Just enough separation" in the case of pure reusable data objects with client-specific attributes for like validation that would be neat to have defined on the class but maintained in a separate piece of source code.
+1 for Code generation of partial classes and handcode additional members for these classes in separate files.
+1
+1 I have large base of methods in our js api service, it would be best to split them into files, which makes it easy to understand each method.
api service like fb (facebook)
or gq (google analytics)
, where you are given one global class or object, which you can use through out your development.
+1
This feature would be a big plus for us too.
We developped a middleware where all types of clients can connect to, and communicate with our servers through it.
We generate some of the client code based on what is exposed by the servers.
So far, everything was ok. But now, we'd like to be able to transfer heterogeneous collections of objects (but with a same base class). The objects transfered are part of the code generated based on the server API.
To be able to use the power of inheritance, abstract methods are the key in this case. But our developpers won't add methods to generated code, i presume everyone knows why ;)
As far as i understood (i'm a C# developper), the solution proposed here (https://github.com/Microsoft/TypeScript/issues/9) would not allow us to do that.
So, partial classes would be perfect for us. We would generate the class as partial, and if developpers need to add logic or methods, they would just have to write an other part wherever they want.
+1
+1 very helpful for code generation
I wonder if module augmentation basically negates the need for this as a specific feature.
If you have a generated class then you should be able to do something like...
import {MyClass} from "./MyClass.generated"
MyClass.prototype.partialMethod1 = function() {
return true;
}
MyClass.prototype.partialMethod2 = function(abc: string) {
this.doSomething(abc);
}
declare module './MyClass.generated' {
interface MyClass {
partialMethod1(): boolean;
partialMethod2(abc: string): void;
}
}
@disshishkov That looks like a good way to implement it under the cover, but I'd still like proper first-class support in the language without the need to manually modify prototypes and maintaining that interface.
I agree with Elephant-Vessel. The manually crafted part of the partial class should be as loosly coupled (at design time in TS) as possible form the generated part.
The suggestion by @david-driscoll works for many cases. However, as far as I can tell, prototypes don't have access to internal variables. This limits the usefulness of this approach in code generation contexts where you want to generate the core parts of your classes and then augment them with custom code.
+1
@david-driscoll that looks like an excellent way to desugar this feature. (would need to add the ability to access to private/protected properties). Wonder what would happen with multiple partial class declaration? What would the mutual visibility of those be?
++ 1 Lack of partial is really hurting me when trying to extend my auto generated models..
I think the only thing in scope here is basically what @david-driscoll proposed -- you could declare new methods (which would be put on the prototype) and new non-initialized properties (which have no codegen), but not new initialized properties (because these have side effects on the constructor codegen).
downvoted, pardon my french, partial classes are a stupid attempt to chunk your god classes, which are inevitable in OOP, into multiple files so that they are easier to manage (although still being god classes)
with OOP there is no future for you people (been there, know what i am talking about)
come to FP, we have cookies and a way to write a program without a single class and s**t like this
So we already basically allow this anyway through interface A { method(): void } A.prototype.method = function() { };
; codifying this into a sugar for exactly that makes sense.
One thing to bikeshed is the keyword partial
. A brief historical vignette: In C#, partial class
was originally going to be extension class
(which you put on all but one declaration), but there wasn't a clear distinction about which declarations would be the extension
and which declaration would be the non-extension declaration. Instead you have the modifier partial
that you must place on all classes.
This is not the case in TypeScript; rather the opposite. Here, only one class (let's call it the "primary declaration") may have constructors / initialized member fields; the primary declaration doesn't get any new modifier. Every other declaration (let's call them "extension declarations") may only have statics, methods, and non-initialized fields, and you'll need to have some modifier on them to indicate that they're not primary.
The current best guess is
class Foo {
x = 6; // only legal to initialize here
constructor() { } // only legal to have constructor here
someMethod() { }
}
// vvvvvvvvv thoughts?
extension class Foo {
someOtherMethod() {
}
}
:sparkles: :bike: :house: :sparkles:
Bikeshedding or not, this is sounding an awful like #311 which was walked away from because of likely interfering with future ECMAScript standards. Why is this worth considering, but proper mixin support isn't?
Using the keyword extension
conflicts with extension methods? Or is this going to negate extension methods completely?
@kitsonk I think the mixin proposal has a lot more open questions. The partial class proposal here is just a codification of things that are already allowed in TypeScript, just with some syntactic sugar (this feature could literally be done as a syntactic remapping to things we already have).
@Elephant-Vessel I don't think extension methods are happening if partial classes happen. Classes are the only place where extension methods make sense given our constraints, and the proposed ES7+ bind operator is a better way to shoehorn in extension-like syntax.
@RyanCavanaugh: to me, it seems a bit constraining to require that one class is primary and the others not. If you want to partition your code so that one file contains all the methods of a class and the other contains all the properties, neither seems more obviously primary, and why should you be forced to choose? Isn't it cleaner to have partials where they're all equal in importance, and merged by the compiler?
@RyanCavanaugh ... I would do it exactly the same way c# does it... it works great for code generation... which i believe was the primary problem the feature meant to solve in c#... although my memory is fuzzy... never had any issues, it's been around for years, and is battle tested...
If partial/extended classes won't be able to have initialized fields, I think it would be extra neat to support something like partial methods that we know from C#, basically a lightweight contract specific for partial classes. So like:
public partial class MyGeneratedClass
{
partial void Initialize();
public constructor()
{
//...
this.Initialize();
}
}
public partial class MyGeneratedClass
{
partial void Initialize() { //... }
}
The need to be able to partialize/extend third-party classes could be quite easily(?) solved by making the partial/extends
generally optional but required for use of partial methods.
I think that this could bring some neat expressive and operative power to the concept that we want here.
Those who's looking for mixins.
Consider the object initializes proposal by means of which (and flow analysis) the mixins can be done elegantly, naturally, idiomatically right as far as JavaScript goes:
function enableBeingPositioned<a>(
// hypothetical syntax
something: a /* <-- before type */ => a & { x: number; y: number; } /* <-- after type */
): void {
something.x = 0;
something.y = 0;
}
let value = {};
value.x; // <-- should not typecheck
enableBeingPositioned(value);
value.x; // <-- should typecheck
Languages such as Java don't offer partial classes/structs. It is a niche feature of C#. Imitating C# "partial class/struct" concepts to full extent is the best way to go here.
In pre-compilation step, the first thing the TS compiler could do is to stitch the partial code blocks in memory and then move on to the regular compilation pipeline route. This will be more than enough to qualify for v1.0.0-preview1.0000
of Partial Classes feature in TypeScript.
Anything beyond what C# is offering is something that can be introduced in later versions when the feature will evolve and outlive its trial/preview. The corner cases and the similar kind of obscure stuff can be discussed separately one at a time and we can worry about the fit&finish later... IMO.
I also agree doing both partial and extension classes exactly the way c# does it is the best way to go in terms of syntax and semantics, both work extremely well for their respective use cases. Partial classes improve the usability of generated code. Extension methods improve the usability of static methods that operate on interfaces, specified generics and third party types. Both are really cool, but please remember they are not the same feature, I'm saying this here because of the proposed syntax above.
Extension methods would allow one to do this (sry about lack of formatting, typing on a phone):
extend MyType[] { somethingSpecificToMyType() { ... } }
extend WeirdThirdPartyDatastructure
That among other things like being able to do use libs like underscore without the cruft, which basically means reaping the syntactic benefits of extending built-in types without actually poluting them.
So brief - lets not treat the two as one and the same, they are both very cool but not the same thing. I'd personally love to have both in our language.
By the way, as a side note, I imagine many people cringe at the idea of code generation and are ready to explain to everyone that it has no place in javascript because there are more idiomatic ways to achieve the same. There are two ways in which code generation is of great help in some projects, such as the one I'm working on right now.
As another side node for the benefit of anyone wondering what partial classes have to do with codegen, the value of keeping the generated parts of a class into a separate file comes from source control requirements - the generated stuff is usually a rapidly changing build artifact and you don't want it committed in your repo.
I like the idea of implementing this accordingly to a well known and tried model of this concept, just like what C# has. 'Imitation' has some negative connotations to it, but it has significant value if done intelligently. On the other hand, the 'intelligent'-part of that is to be aware of different circumstances and limitations, so I won't protest if partials for TypeScript does not exactly look like partials for C#.
@Elephant-Vessel, I agree that it needn't be a ditto copy of C# partials, the general design can be laid out first crafted around TypeScript / JavaScript flavor while taking maximum inspiration from C# partials. My suggestion is to go with the 'registering partial
keyword' and 'stitching partials' as a first step, and ship it as an experimental/preview feature (so consumer don't start depending on it in production code right off the bat). Later, based on the community response, evolve the feature until it is prod-ready and RTM'd. If we worry about all kinds of tricky gotchas and scenarios before hand, then most probably it will delay the matter further.
+1 - For extending and maintaining generated objects.
+1 for maintaining generated code
I created polyfill and divided my large class.
Define a class as an interface.
export class C {
constructor() {
}
}
export interface C {
m(): void;
}
Implement class members.
export default class extends C {
m(): void {
}
}
Merge implementations.
import {C} from './core';
import m from './member/m';
compose(C, m);
export {C}
import {assign} from './assign';
import {concat} from './concat';
export function compose<T extends new (...args: any[]) => any>(target: T, ...sources: T[]): T {
return concat([target], sources)
.reduce((b, d) => {
void assign(b.prototype, d.prototype);
for (const p in d) if (d.hasOwnProperty(p)) b[p] = d[p];
return b;
});
}
https://github.com/falsandtru/spica/commit/a6ff30da5319db5f25f703a29da48fc0f7dbe2fe
I think this is a terrible idea for one specific reason: it will make the already complicated rules for global, ambient, external, namespaced, and brittle declaration order dependent inheritance problems significantly worse. This is not C# and name resolution and member declarations is very different.
Perhaps use an object or namespace instead of a class, a la the revealing module pattern if you really need to do this. Otherwise your class is probably just too large.
Also decorators can be used to implement associations between generated code and hand written code.
Add support of partial classes. Mixins is not the same, because it's run-time realization. Need compile realization, where partial classes will be combine into one before converting typescript to javascript.
will be: