Skip to content
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

Improve "configuration" programming #8083

Closed
tinganho opened this issue Apr 14, 2016 · 6 comments
Closed

Improve "configuration" programming #8083

tinganho opened this issue Apr 14, 2016 · 6 comments
Labels
Duplicate An existing issue was already created

Comments

@tinganho
Copy link
Contributor

If you make very good abstraction of a framework. You will reach a point where consumers of your framework more "configure" than "code" using your framework. Your consumers extend classes and these classes often talks to some other classes of your framework so they need some kind of contract or protocol to be implemented.

This is very common among many framework. Take whatever framework ASP.Net MVC, iOS, etc. etc..

To just give you an example here is a PeopleList class:

class PeopleList extends List {
    protected title(p1: Type1, p2: Type2, p3: Type3): number {
    }
} 

There are several problem with the above example and it really don't conform to the "configuration" programming I have in mind. A consumer needs to know the following to configure the title property:

  1. Visibility.
  2. Parameters types.
  3. The return type.

When a consumer need the know all of these things. It doesn't really sound like a configuration. One should only need to write:

class PeopleList extends List {
    title(p1, p2, p3) {
         return `Hello ${p1}, ${p2} and ${p3}.`.
    }
} 

There is a PR #6118 that implements contextually typing for implemented interface members but this is not what I'm looking for.

First, when I extended the List class above. It should already know which protocol or contract. One shouldn't need to write an implements clause. Second, the List class needs those properties too and I cannot really implement the interface in my base class, because the implementation is reserved for sub classes of List. The implements interface for protocoling is really only for other non-related classes/function/etc. It is not made for base classes. Here a lot of abstraction is provided in the base class. How can one make them aware of theses properties?

One solution is abstract classes. Though one of the pain points of abstract classes is that it is more like "you must configure all of these properties". So you cannot implement optional configurations(I proposed optional abstract members in #6413). It also doesn't deal with the configuration style I have in mind mentioned above, that a consumer doesn't need to know the visibility or the types of the signature.

Solution

  1. Contextually type parameters and return types and visibility from base class.

    class A {
        protected a(b: string): string {
            return '';
        }
    }
    
    class B extends A {
        a(b) { // 'a' is protected and returns a string. 'b' is of type string.
            return '';
        }
    }
    
    let b = new B
    b.a // error a is protected
  2. Allow signature declarations(functions without bodies) in abstract classes. Also allow to declare them as optionals.

    contract class List {
        config protected title(p1: string, p2: string, p3: string): string; // title is required.
        config protected description?(p1: string, p2: string, p3: string): string; // description is optional. A dev can decide to implement it or not.
    }
  3. Contextually type properties. And also make them required or optional for a sub class.

    contract class List {
        config length: string;
        config paddingBottom?: number;
    }
  4. Apply all the rules above to statics as well.

    contract class Page {
        config static setPageHeader(requestInfo: RequestInfo): PageHeader;
    }
  5. Maybe you just don't want a protocol to be implemented in order for you to communicate within a base class. You might want to communicate with external code as well:

    interface ListProtocol {
       title(p1: string, p2: string, p3: string): string; // title is required.
       description?(p1: string, p2: string, p3: string): string; // description is optional. A dev can decide to implement it or not.
    }
    contract class List uses SomeProtocol {// List now uses ListProtocol but doesn't implements it.
    }
    
    function renderList(list: ListProtocol) { the list parameter conform to the ListProtocol
        // do something with it
    }

Consider contract classes as abstract classes on steroids. You can also consider reuse the abstract keyword. Though @mhegazy told me before that the meaning of abstract is more like required. Though I think contract has more precise meaning than abstract in this case.

More examples:

To just give you an another example of the problem here is the .Net MVC framework:

When a consumer need to write the following very long signature it really isn't about configuring.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
}

And practically every View or Model class in any UI framework have at least some kind of configurations that needs to be made. Some frameworks have less configurations some more. I don't want bother give you more example. But I have choosen to adopt more configuration coding in my frameworks. It is more "magic" happening. But you need to write less boilerplate code. Though it would be great if TypeScript really could solve some of the painpoints I have.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 14, 2016

I think most of this request (modulo new syntax) is covered in #1373 and #3667 which we have accepted the design for a few weeks ago.

@tinganho
Copy link
Contributor Author

#1373 doesn't deal with visibility? And it just deals partly of point 1. #3667 has the problem I described above:

First, when I extended the List class above. It should already know which protocol or contract. One shouldn't need to write an implements clause. Second, the List class needs those properties too and I cannot really implement the interface in my base class, because the implementation is reserved for sub classes of List. The implements interface for protocoling is really only for other non-related classes/function/etc. It is not made for base classes. Here a lot of abstraction is provided in the base class. How can one make them aware of theses properties?

Or more precisely contextually type of implemented interface members just deal with the most sub classed class. How is the base class which provides the abstractions going to have those properties?

Note, I'm looking for a clean solution. Maybe abstract and declaration merging can deal with most problem described above, though I see declaration merging as a very hacky solution. Ideally I also want a solution to contextually get the visibility.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 14, 2016

doesn't deal with visibility?

i do not know about visibility. everything is public by default. and this seems right to me. making an inherited property protected vs public does not impact the "contract" with the base class. it just exposes more stuff. so i do not see the problem here.

How is the base class which provides the abstractions going to have those properties?

mark them as abstract ?

@tinganho
Copy link
Contributor Author

tinganho commented Apr 15, 2016

i do not know about visibility. everything is public by default. and this seems right to me. making an inherited property protected vs public does not impact the "contract" with the base class. it just exposes more stuff. so i do not see the problem here.

Can it default to the visibility of the base class? I think it is always better to expose as little as possible? Not a huge deal breaker for me. But why not aim for perfection?

mark them as abstract ?

I was talking in the context of implementation of interfaces. Thought first, only implemented interfaces was going to be contextually typed. Now I see base members will also be. That's great.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 15, 2016

I still do not see the point about visibility. it is an error to have something less visible than your parent. and by default everything is public. if you want to hid things lower the visibility and you will get the correct error. i do not see this as a blocker to implementing/using a framework.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 15, 2016

Closing in favor of #1373 and #3667

@mhegazy mhegazy closed this as completed Apr 15, 2016
@mhegazy mhegazy added the Duplicate An existing issue was already created label Apr 15, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

2 participants