Skip to content

Improve "configuration" programming #8083

Closed
@tinganho

Description

@tinganho

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions