Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

  • MrHighTech
    started a topic TypeScript integration

    TypeScript integration

    hello Isomorphic and all,

    I am currently playing with TypeScript and I actually see value with it. There are a couple of older posts here about using the referenceDocs(js/xml).

    I am working on that and will contribute it to DefinitelyTyped when it is completed. I think a first level integration should be available this week for testing, but it might take a bit more to have a nice integration with the create methods (Interfaces will help there).

    The reference docs are pretty good, but I am having a problem with inheritance. Looking at the documentation for ListGrid for example lists it as extending VLayout. But the field inheritsFrom is not present (only there in a couple of places).

    The implementsInterfaces are also present, but I haven't checked them thoroughly yet.

    I was under the impression the online documentation is generated form the referenceDocs... Any ideas on how to get a reliable hierarchy from the the file ?

    Thanks!

  • Isomorphic
    replied
    As we've covered, we did not intend to define a new JavaScript primitive type, rather, boolean vs Boolean is just a documentation convention that indicates whether null is allowed. Using this convention eliminates a lot of redundancy.

    Glad you figured out how to best represent whether null is allowed in TypeScript.

    If there are any inconsistencies left with boolean vs Boolean in subclasses, we'd recommend allowing null.

    Leave a comment:


  • kylemwhite
    replied
    RE: BOOLEANS

    boolean is a JavaScript primitive type.

    In JavaScript, it can contain the values true or false. It can be assigned other values such as undefined or null (or a string or whatever) but its type will change. But, since there's no type checking in JavaScript, nothing breaks until (maybe) runtime. In JavaScript, boolean can not be redefined. Same with TypeScript.

    In TypeScript, if strictNullCheck="false", then boolean can be true, false, undefined or null. If strictNullCheck="true", then boolean can only be true or false.

    Thus, setting strictNullCheck="true" in TypeScript accomplishes the goal of limiting boolean to true or false.


    from https://developer.mozilla.org/en-US/...bjects/Boolean
    In JavaScript, the Boolean object is an object wrapper for a boolean value.
    Any object of which the value is not undefined or null, including a Boolean object whose value is false, evaluates to true when passed to a conditional statement. For example, the condition in the following if statement evaluates to true:

    Code:
    var x = new Boolean(false);
    if (x) {
      // this code is executed
    }
    This is probably not the intended behavior of Boolean in SmartClient (or is it?)

    JavaScript IS TypeScript, thus the Boolean in TypeScript behaves the same as the Boolean in JavaScript unless strictNullCheck="true".

    With strictNullCheck="true", the Boolean object can no longer be set to undefined or null and this would violate the ISC definition of Boolean.

    So, in order to obtain the functionality documented for boolean and Boolean without trying to redefine JavaScript types, I am now doing the following:

    1. Set strictNullCheck="true". This is actually not defined in the typedef library, it is (usually) defined in the tsconfig.json file for a TypeScript project. Somewhere in the documentation, I will recommend to the developer that they use strictNullCheck="true" but that will be the developer's choice.
    2. boolean will remain unaltered. You can't redefine it anyway. With strictNullCheck="true", it will be limited to true or false.
    3. Boolean types will be mapped to boolean | null as described in the ISC documentation. This will allow the value to contain true, false or null as intended and still behave like a boolean (not like an object).

    This effectively eliminates the JavaScript Boolean object from SmartClient but I think that's ok because ISC is not defining any new functionality for it anyway.

    If a developer does not use strictNullCheck="true", then he/she may have code that sets boolean values to null or undefined... although it will still be documented that they should not do that.
    Items defined as Boolean will also be able to take the value undefined, which is not the intent but again, the documentation will discourage that.

    Similarly, items of type Number have been generated to be Number | null so that they can be null even when strictNullChecks="true" and conform to the ISC system. Note that they are not replaced with number | null because if Number is specified, then it is implied that the extensions on Number are available, which wouldn't be the case if number was used.

    number will remain a JavaScript primitive type which, when strictNullCheck="true", will not allow undefined or null. This is not defined in the ISC docs (and doesn't need to be) but I'll bet that if it was, it would be similar to boolean in that it is not intended to hold the values null or undefined.

    This method not only documents the intended behavior in the property type definitions (i.e. | null is explicit), it actually leads to enforcement of the intended behavior (when strictNullChecks="true"). It should be noted, however, that there are many cases of subclasses defining properties and methods with types/signatures different than the parent class (even casing differences count because boolean is not the same as Boolean) preventing generation of properties and methods. I know that sometimes this is by design but I suspect that most of them are just inconsistencies. For those that are inconsistencies, they should be easy to fix. For those that are intentional, I'm still working on a way to deal with that but I'd like to get rid of the inconsistency cases first to see how many we're dealing with. They are, of course, all documented in the errors.txt file.

    Leave a comment:


  • Isomorphic
    replied
    We've covered Boolean vs boolean, Integer vs int before: our recommendation remains that, since the distinction can't be directly represented in TypeScript, that the generated API included a generated phrase like "Cannot be null" so the developer doesn't miss out on the information.

    We'll check on uses of "integer" (not capitalized).

    @methods on the Callback object do get used as types: previously we were using them as types for params (eg Callbacks.LoadScreenbCallback et al), and now in a couple of places as the types for attributes where a String expression or Function can be supplied. It seemed the clearest way to provide a definition for the expected behavior of the Function that can be supplied.

    Leave a comment:


  • kylemwhite
    replied
    Booleans: Understand that Boolean can be null whereas boolean cannot. However, boolean conflicts with the JavaScript primitive type boolean so it cannot be declared as an additional type in TypeScript. I will just ignore it. The only loss is that there's not a way (that I can think of yet) to attach the 'not null' documentation to the primitive boolean type and TypeScript cannot enforce the 'not null' rule but that's no worse than JavaScript so not a big deal.

    Ints: Understand that Integer allows nulls whereas int does not. How about integer? I don't see a definition for that but it is used a lot (I get 205 hits when I do a case-sensitive search).

    Callbacks: Generally @method definitions are not used as types (if I'm wrong about that, please give me an example as I may have a misunderstanding). However, I understand that Callbacks is special and its purpose is to define method definitions as types so I'll make it work.

    Leave a comment:


  • Isomorphic
    replied
    With Boolean vs boolean, Integer vs int we are conveying primitive vs object type, as in Java. So the lowercase variants mean no nulls. The docs explain this as well.

    We'll check on whether there are letter case issues (3 and 4).

    By defining those Callbacks as methods on the Callback object you get a full @method definition, which you should be able to translate to a TypeScript definition as you can with any of our other method definitions.

    Leave a comment:


  • kylemwhite
    replied
    I see a lot of changes, especially with defining types, which is good. It has broken my generator for now, but that's ok. I am removing lots of special cases from the code which is great. With these changes, I am encountering a few new errors but I think they're easy fixes so instead of writing special cases to handle them, I'm hoping to just get them fixed in ref docs.

    1. Why define boolean as a type? It's already a Javascript primitive. The way it's defined, it basically tries to make boolean equivalent to Boolean, which is not the case in Javascript. I would just have to remove it.

    2. Using Integer, integer and int interchangeably. How about picking one and sticking with it? I vote for Integer because it's descriptive and types usually start with a capital.

    3. <docItem ref="type:HTMLString" type="type" description="A String of HTML,..." name="HTMLString" baseType="string"></docItem> <-- Should be String?

    4. SCImgUrl and SCImgURL should have the same casing everywhere (unless they are two different types, but I doubt that).

    5. ValidatorConditionCallback, GetFieldValueCallback and ValidatorActionCallback are methods on the Callbacks class but they are used as types in the DataSourceField, ValidatorDefiniton and Validator classes. Can these be defined as types? I'm not sure what that would look like in the ref docs but in TypeScript it would look like this:

    Code:
    export type ValidatorConditionCallback = (item: DataSourceField | FormItem, validator: Validator, value: any,  record: any) => boolean;


    Leave a comment:


  • Isomorphic
    replied
    isA methods have been changed to staticMethods in the latest builds of 12.0d.

    Leave a comment:


  • kylemwhite
    replied
    Yes, I realize now that it's always been its own object (or class), so.... non-issue.
    Yes, the only issue is that the static methods should be called staticMethod instead of classMethod because isA is an object and not a class.

    Leave a comment:


  • Isomorphic
    replied
    It looks like isA has been an object for a long time and many releases, so if there's any issue, it's just that the existing methods on it have been left as classMethods (since the concept of staticMethods didn't exist previously). Is that what you're saying?

    Leave a comment:


  • kylemwhite
    replied
    I see that several Classes that did not inherit from Class are now tagged as "objects". This makes sense. I also see that for these objects, what used to be classMethods are now called staticMethods. All good. I've modified the generator to handle these. Thanks.

    However, I also see that isA has been moved to its own object, which is fine, but the the methods are marked as classMethod. I think they should be staticMethod. This probably accounts for most of the decrease in the Methods Generated count which went from 3298 to 3278.


    Click image for larger version

Name:	isA class methods.png
Views:	1
Size:	63.6 KB
ID:	248787

    Leave a comment:


  • kylemwhite
    replied
    We use only "DOMElement" (not those other variations) and an API that takes DOMElement or String will have the type "DOMElement | String" (consistent with all other alternative types).
    Code:
    <docItems scVersionNumber="12.0" scVersion="12.0d" versionNumber="SNAPSHOT_v12.0d_2017-08-09" version="SNAPSHOT_v12.0d_2017-08-09/AllModules Deployment">
    ...
    <docItem type="method" definingClass="class:GridRenderer" ref="method:GridRenderer.getCellFromDomElement" description=" Given a pointer to an element in the DOM, this method will check whether this&amp;#010 element is contained within a cell of the gridRenderer, and if so return a&amp;#010 2 element array denoting the <code>[rowNum,colNum]</code> of the element&amp;#010 in question.&amp;#010" flags="A" name="getCellFromDomElement">
    <returns type="Array" description="2 element array containing rowNum and colNum, or null if the element is not contained in any cell in this gridRenderer"/>
    <groups>autoTest</groups>
    <params optional="false" type="DOM Element" description="DOM element to test" name="element"></params>
    </docItem>
    ...
    <docItem ref="method:ActiveXControl.getPluginHandle" type="method" definingClass="class:ActiveXControl" description="  Returns a handle to the element for this ISC ActiveX control object.&amp;amp;#010" flags="" name="getPluginHandle">
    <returns type="HTML Element" description="pointer to the plugin element in the DOM"></returns>
    </docItem>
    
    <docItem ref="method:Flashlet.getPluginHandle" type="method" definingClass="class:Flashlet" description=" &amp;amp;#010  Returns a handle to the flashlet DOM element (valid only after the component has been drawn).  &amp;amp;#010&amp;amp;#010" flags="A" name="getPluginHandle">
    <returns type="HTML Element" description="pointer to the plugin element in the DOM"></returns>
    </docItem>
    
    <docItem ref="classMethod:EventHandler.getNativeMouseTarget" type="classMethod" definingClass="class:EventHandler" description=" Returns the natively reported target (or source) DOM element for the current mouse event.&amp;amp;#010 &amp;lt;b&amp;gt;NOTE:&amp;lt;/b&amp;gt; SmartClient cannot guarantee that the same element will&amp;amp;#010 be reported in all browser/platform configurations for all event types.&amp;amp;#010 If you wish to make use of this value, we recommend testing your use case &amp;amp;#010 in all target browser configurations.&amp;amp;#010&amp;amp;#010" flags="A" name="getNativeMouseTarget">
    <returns type="HTML Element" description="native DOM element over which the mouse event occurred"></returns>
    </docItem>

    we could add offical @type declarations (with @baseType Number) so that you have a source for a description to generate into the TypeScript headers.
    I don't understand what this means but I want to. Please elaborate.

    We don't *think* we have any more instances of subclasses overriding with the wrong type (or different casing). This doesn't seem to be something you're flagging in Errors.txt.
    I will try to find these programmatically and put them in the Errors.txt file. In the meantime, here's the TypeScipt build output when I don't convert Boolean to boolean:

    Code:
    Build:Interface 'MenuItem' incorrectly extends interface 'ListGridRecord'.
    Build:Interface 'CubeGrid' incorrectly extends interface 'ListGrid'.
    Build:Interface 'DynamicFormProps' incorrectly extends interface 'CanvasProps'.
    Build:Interface 'DynamicForm' incorrectly extends interface 'Canvas'.
    Build:Interface 'EventCanvasProps' incorrectly extends interface 'VLayoutProps'.
    Build:Interface 'EventCanvas' incorrectly extends interface 'VLayout'.
    Build:Interface 'MenuProps' incorrectly extends interface 'ListGridProps'.
    Build:Interface 'Menu' incorrectly extends interface 'ListGrid'.
    Build:Interface 'MenuButtonProps' incorrectly extends interface 'ButtonProps'.
    Build:Interface 'MenuButton' incorrectly extends interface 'Button'.
    Build:Interface 'MiniDateRangeItemProps' incorrectly extends interface 'StaticTextItemProps'.
    Build:Interface 'SelectItemProps' incorrectly extends interface 'FormItemProps'.
    Build:Interface 'SelectItem' incorrectly extends interface 'FormItem'.
    Build:Interface 'SliderProps' incorrectly extends interface 'CanvasProps'.
    Build:Interface 'Slider' incorrectly extends interface 'Canvas'.
    Build:Interface 'SplitbarProps' incorrectly extends interface 'StretchImgProps'.
    Build:Interface 'Splitbar' incorrectly extends interface 'StretchImg'.
    Build:Interface 'ToolbarProps' incorrectly extends interface 'LayoutProps'.
    Build:Interface 'Toolbar' incorrectly extends interface 'Layout'.
    Build:Interface 'ToolStripProps' incorrectly extends interface 'LayoutProps'.
    Build:Interface 'ToolStrip' incorrectly extends interface 'Layout'.
    Will revisit Callbacks and AutoChild.

    Leave a comment:


  • Isomorphic
    replied
    We use only "DOMElement" (not those other variations) and an API that takes DOMElement or String will have the type "DOMElement | String" (consistent with all other alternative types).

    We don't have any plans to change from using informative type names like "Integer" and "Float", nor do we plan to inject thousands of copies of an identical description of what these types mean into the refDocs file. However, we could add offical @type declarations (with @baseType Number) so that you have a source for a description to generate into the TypeScript headers. This would also allow you to get rid of these last (very small) lists. The rest have been obsolete for some time.

    We don't *think* we have any more instances of subclasses overriding with the wrong type (or different casing). This doesn't seem to be something you're flagging in Errors.txt.

    "Callbacks" is neither a Class nor an Object, it's just a way of organizing the docs. This one item needs to be flagged as something to be ignored in terms of API generation.

    Take a look at the AutoChild docs again: if something is documented as an AutoChild, it implies multiple related APIs exist, which are not necessarily explicitly, separately documented. So it's not just a usage hint. It's true that we could have a separate attribute "autoChild=true" but this doesn't materially change the work of a generator so we don't plan to change that now.





    Leave a comment:


  • kylemwhite
    replied
    5. "AutoChild" and "MultiAutoChild" are not really "extra" words - although you probably need to strip them out to figure out the TypeScript type, it makes sense to provide a link in the generated doc to the AutoChild / MultiAutoChild usage information, as we do in the SmartClient Reference
    Understood they are not "extra" words to you but for the purposes of inferring the type, they are meaningless. They describe how/when a property is created but not what type it is. An AutoChild array of items is just an array of items. It just happens to be automatically created. And of course it makes sense to have a link to explain what they are. I would have, however, just added an attribute to the docItem that marks it as an AutoChild instead of putting it in the valueType field. But that's just me.

    Leave a comment:


  • kylemwhite
    replied
    1. DOMElement is not a string type, this refers to native DOM elements as returned by standard DOM APIs like getElementById(). TypeScript surely has something appropriate to map this to.
    Understood. TypeScript is mostly just decorated JavaScript. All the types we're talking about are native JavaScript types. There is no "TypeScript equivalent", there's just the JavaScript Element. To capture these types accurately, I now have the following additional list:

    Code:
     string[] elementTypes =
    {
       "DOMElement", "DOM Element", "domelement", "HTML Element"
    };
    I know that in some cases (maybe all?), the parameter can take an Element or a string. In those cases I would recommend setting the type as Element | string.

    2. Integer, Float etc are not just variants of Number, they convey that only integral or decimal values are expected and supported. If there is no corresponding TypeScript notion, we would suggest mapping to Number but then adding a note in the doc ("Decimal value expected and allowed" or similar)
    There is no TypeScript-specific notion of number, just the JavaScript number so these all map to number. Agreed that it should be documented on what type of number (int, float) but disagree on where to put that documentation because it could be as simple as "integer" or more complicated like "positive integer between 1 and 10". I suggest putting it in the description (i.e. "Decimal value expected and allowed") but I already know that's not going to happen. In the generated code, if the actual inferred JavaScript type is different than the SmartClient type, I put the original type in comments. For example:

    Code:
    readonly maxHeight?: number /* integer */;
    3. similarly, "Boolean" vs "boolean" conveys whether null is allowed and expected (and the one typo of "bool" has been fixed for a long time)
    Yes, bool is gone, thanks. If, however, I retain boolean and Boolean, a lot of stuff breaks because there are many subclasses that redefine a property from one to the other. I don't know if this is intentional or not as there are lots of casing issues throughout. If I just map everything to boolean, it works and I don't think it has any negative consequences. If you can think of some, please let me know.

    4. any method defined on the "Callbacks" object can be assumed to be a callback. This is a SmartClient idiomatic way of defining things, but should not require a list of hardcoded callback names (which would be fragile)
    Until now, I was not aware of the Callbacks object (or class?). I see now that it contains many callbacks with parameters. This is good because I can use it to generate more detailed, accurate definitions (although it will take some work). The docs have it defined as a Class but in the description says, "This object cannot be used....". So is it a Class or an Object? ... and if it's a Class, does it inheritFrom Class?

    Agree that all of these lists are fragile and I wish I didn't have to make them in the first place. But, unfortunately, there is no field in the refDocs that just tells me what the actual JavaScript type is so I need to maintain (hopefully decreasing) lists in order to map the types to something the compiles.

    Leave a comment:

Working...
X