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

Introduce a way to enforce a static contract #1263

Closed
Elephant-Vessel opened this issue Nov 25, 2014 · 9 comments
Closed

Introduce a way to enforce a static contract #1263

Elephant-Vessel opened this issue Nov 25, 2014 · 9 comments
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@Elephant-Vessel
Copy link

I'm building an application framework in javascript and just porting it over to typescript to use some of that sweet static typing. In my framework I happen to have a concept of a UI module. A UI module is a class and can be instantiated many times in an application. All modules have static definitions of their nature like what events they can trigger and what internal models they make publicly available. The implementation of these modules is a application-implementaional part on a level above the framework codebase, but the framework needs to treat the modules polymorphically (non-instantiated) to read these definitions to set up the application engine accordingly.

If there was the possibility to define static fields in an interface this would take care of making this contract explicit and statically validated for the application developer. But we don't have that. I'm sure that this contract could be enforced by wrapping the module classes in some kind of factory pattern or other contraptions, but I'm honestly not very attracted to that.

Wouldn't it be neat if we can enforce a static contract in typescript, considering the dynamic nature of the underlying javascript where this kind of static type polymorphism isn't all that weird?

@danquirk
Copy link
Member

Interfaces can't have statics because they only define what the instance side of a type looks like. Why can't you model these types with classes with statics?

@Elephant-Vessel
Copy link
Author

Well would there be any problem in adjusting the responsibility of the interface? I can only see that it would be quite trivial to allow signatures in an interface to be declared with the static keyword, and that this would be semantically quite clear about it's meaning.

"Why can't you model these types with classes with statics?" A little unsure about what you are asking here, but as I wrote above the classes needs to have both static and instance members, but I can only enforce a static ((pre-)compile-time) contract on the instance members by using an interface. Even if the framework requires that all these classes implements some static functions, I can not enforce this compile-time.

Isn't the only reason that we don't have static interface signatures in languages like C# simply because it makes little sense since we always have to call these explicitly on the specific class anyway? Which is not the case in javascript, where we can have an arbitrary collection of uninstantiated classes/types and handle them just like any other variables.

Simply put: We can do static type polymorphism in javascript, but currently we have no way to enforce a contract on this in TypeScript. And my guess is that it would be relatively easy to implement by allowing static signatures in interfaces.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Needs More Info The issue still hasn't been fully clarified labels Nov 26, 2014
@RyanCavanaugh
Copy link
Member

I'm uneasy on static in an interface. If you think about code like this:

interface PointLIke {
  static zero: PointLike;
  x: number;
  y: number;
}
var x: PointLike = { x: 3, y: 2 };

I have no intuition about whether that should be an error or not. The object literal doesn't have any static members, but the very concept of a static member on an instance just doesn't make any sense. Does static just mean "When used in an implements clause, enforce this, otherwise ignore" ?

This is reasonably easy to fake with something like this:

interface HasParse<T> {
   parse(s: string): T;
}
class MyDate {
  static parse(s: string) { return new MyDate(...); }
}
// Will give an error if MyDate does not fulfill the static contract
var __myDateHasParse: HasParse<MyDate> = MyDate;

Obviously that's not entirely optimal (maybe you put the checks in their own file that gets trimmed out of the final build output or something) but it accomplishes the task without burdening interface with new and weird semantics.

@Elephant-Vessel
Copy link
Author

Hmm... Yes, the static duck-typing makes it a little more rugged terrain obviously.

I guess the core of the problem is that an interface both works as a contract for a class and as a duck-typed contract for an arbitrary object. And a class is obviously a different concept from an object instance, and by the introduction of a static signature in the interface, we would introduce a property of the interface that only applies to one of these two usages, thus muddying up the waters a bit.

One could think that some options could be

  1. A interface with a static signature is explicitly defined as a class-interface, and not usable for duck-typing objects.
  2. The active properties of an interface is dependent of the usage, thus ignoring the static signature when duck-typing objects (which kind of makes sense since the would-be class of the object that the static signature would apply to is irrelevant and non-existing).

And when I think about it, option 2 isn't all that bad actually. One could think of the implicitly imagined class of the duck-typed object as being in some unobservable quantum-state, fulfilling all and no interfaces at the same time, because we really can't observe it anyway.

I think this can make sense considering the nature of js. But maybe it would just look quite strange for a lot of people. And be rarely used anyway.

@danquirk
Copy link
Member

danquirk commented Dec 1, 2014

I'm still not really clear why you can't use classes to model these contracts you're describing. The interfaces you are complaining about not having access to statics could be classes with statics.

@RyanCavanaugh RyanCavanaugh added Too Complex An issue which adding support for may be too complex for the value it adds and removed Needs More Info The issue still hasn't been fully clarified labels Dec 1, 2014
@Elephant-Vessel
Copy link
Author

@danquirk, I'm not sure I follow you (and I don't want to come off as someone complaining, just suggesting that static signatures on interfaces might have value considering the nature of js).

Consider this:

module Modules
{
    export class Bootstrap implements Core.iApplicationModule
    {
        constructor(private app: Core.Application, private id: string)
        {
            //...
        }

        private _self = Bootstrap;
        public static get ModuleName() { return "Bootstrap"; } // Can not enforce by contract
        public get ModuleName() { return this._self.ModuleName; } // Can enforce by contract

        public static get EventSpecification():Core.EventSpecification // Can not enforce by contract
        {
            //...
        }

        public EventHandler(event: Core.Models.Event):void // Can enforce by contract
        {
            //...
        }
    }
}

How would you enforce a contract stating that these static methods must exist in this class? Note that they contain custom logic (not able to put this in a base class) and that they only will be called statically directly on the class.

@Elephant-Vessel
Copy link
Author

My heart is still yearning for static contracts. Do you guys still think it's too complex? It's be such a neat feature considering that classes in JS/TS are first-class elements in Javascript, even if it might fell weird for someone coming from a language like C#

@RyanCavanaugh
Copy link
Member

I think we actually approved this - #420 is Accepting PRs. If that feature is correctly implemented, if you wrote

interface HasX {
  x: number;
}
class Foo {
  static x: string;
}
module Foo implements HasX { }

you would get an error about the types of x not matching

@quantuminformation
Copy link

I actually like the idea of the static contract, not just allowing static.

@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

4 participants