-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Programatically Modifying Types #4490
Comments
So... you can actually type your example fairly nicely in TS 1.6: interface MixinFactory<T> {
create: () => T;
}
type AnyCtor<T> = (() => void) | (new() => T);
function compose<A>(ctor: AnyCtor<A>, a: A): MixinFactory<A>;
function compose<A, B>(ctor: AnyCtor<A&B>, a: A, b: B): MixinFactory<A&B>;
function compose<A, B, C>(ctor: AnyCtor<A&B&C>, a: A, b:B, c:C): MixinFactory<A&B&C>;
function compose<A, B, C, D>(ctor: AnyCtor<A&B&C&D>, a: A, b:B, c:C, d: D): MixinFactory<A&B&C&D>;
function compose(ctor: new() => any, ...mixins: any[]): MixinFactory<any> {
ctor.prototype = _.extend({}, ...mixins);
return {
create() {
return new ctor();
}
};
} The only caveat is the arbitrary number of overloads - there's no way to say that it could take an arbitrary number of type arguments and "merge them all" using current syntax. Similarly, there's no way to get the types of an array literal passed through the system. This literal: var myMixins = [{
a: 2
}, {
b: 'twenty'
}, {
doTheThing() {
console.log('The thing!');
}
}]; Has the type var factory = compose(() => {}, ...myMixins); we know that the best overload is Regardless - you can still do this: var factory = compose(() => {
}, {
a: 2
}, {
b: 'twenty'
}, {
doTheThing() {
console.log('The thing!');
}
});
export var __instance = factory.create();
export type MyClass = typeof __instance;
export var create = factory.create; (I personally think that it's cute that you can extract the type in that way, though it would be better if And have full types flowing throughout your code. |
@weswigham thanks! The overloading hadn't fully occurred to me. A couple of thoughts:
I think it has been discussed other places, but potentially a rest operator for generics is a part solution? Though how to operate it it programmatically would still need to be addressed unless it is implied a function compose<...R>(ctor: AnyCtor<R>, /*...*/); Another thought, extracting the type via I mentioned Decorators above too, but I didn't give an example. Here is where I see things becoming difficult: interface AfterAdvice {
(target: Object, result: any, args: any[]);
}
function after(advice: AfterAdvice) {
return function(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> {
let origFn = target[propertyKey];
return {
value: function () {
let result = origFn.call(target, arguments);
return advice.call(target, result, arguments);
}
}
}
}
class A{
@after(function () {
return true;
})
foo(): string {
return 'true';
}
} If I want a decorator to modify the type, I can't. I think the logic is the same for class decorators as well. It would be great if a decorator had some sort of ability to not only install functionality but change the type as well. I guess what I am asking for is that now is to allow operators to work on type aliases... for example: let type MyClass = typeof instance;
MyClass.foo = typeof (a: string, b: number) => void;
/* now MyClass type/interface has a property of foo which is a function */
let myFactory = compose<MyClass>(/*...*/); Then I can do all sort of "magic" including handling the merging of type, intersections, conflict resolution, decoration and manipulation without it having to be supported "natively" in TypeScript... |
Sounds like you'd want |
This really has been solved in lots of other ways. I don't have a legitimate use case anymore for this. |
I see a challenge currently in TypeScript in supporting alternative methods of OO programming. The topic has come up in several forms before, and there isn't a clear solution. Here is a summary of related tickets all in a similar problem space:
Currently, the only ways of "defining" types at design time:
In JavaScript, I can support other non-Inheritance and non-OO programming paradigms, but if I decide to extend those in TypeScript, I almost certainly will lose the static typing
For example, I can create a "composition/factory" in JavaScript, that makes it easy for me to "compose" functionality:
But if I wanted all sorts of TypeScript goodness, it essentially starts to get really complicated. I could of sort of used Generics with Intersections, but the actual definition of the type and the implementation are decoupled and users of the compose library would have to learn some "arbitrary" syntax.
Now if there was a way, behind the scenes, to programatically merge/mixin types and modify them, I could abstract the user from the implementation and it would just work... I would love to be able to write something like this in TypeScript:
Another example would be Aspect Oriented Programming. It is easy to support in JavaScript and actually the Decorators help a lot in TypeScript, but the challenge is that Decorators don't modify the type definition, and with after and around advice the return type can be modified. There is currently no way of expressing this in TypeScript.
There are probably loads of other patterns that are implemented in JavaScript which we cannot easily use in TypeScript and currently, it means us in the community are like a load of needy children, saying "I want, I want, I want, I need, I need, I need". Obviously a lot of this boils down to pseudo-religious debates, but without design time programmatic access to accessing and manipulating the types, we are going to constantly be running into these conflicts.
I am not sure of what the most logical and clean way the TypeScript team could implement something like this, but hopefully the team sees the need...
The text was updated successfully, but these errors were encountered: