loading...

Seriously, Typescript is just annotation!

jwp profile image John Peters Updated on ・5 min read

Which makes life easier, sunny and happy for all attending the picnic. Its power in pre-runtime error checking, intellisense and all the other hats that are hung on annotations is unmatched by plain old JavaScript.

Typescript by Microsoft, is the inventor of the Class, Arrow Functions, Async Await, modules, namespaces, and type inference. Even the popular RxJS came via a migration of Reactive Extensions,created by Microsoft Labs.

As for the Typescript Class, let's see the output of an ECM2015 compile. BTW Typescript easily compiles to 4 or 5 versions of JavaScript.

 var SearchComponent = /** @class */ (function () {
        function SearchComponent() {
            this.debug = false;
            this.showClearButton = true;
            this.placeholder = "Search";
            this.ClearSearchEvent = new core.EventEmitter();
            this.KeyUpEvent = new core.EventEmitter();
            this.SearchEvent = new core.EventEmitter();
            this.faSearch = freeSolidSvgIcons.faSearch;
        }
        /**
         * @return {?}
         */
        SearchComponent.prototype.ngOnInit = /**
         * @return {?}
         */
        function () { };
        /**
         * @param {?} search
         * @return {?}
         */
        SearchComponent.prototype.onClearClicked = /**
         * @param {?} search
         * @return {?}
         */
        function (search) {
            if (this.debug) {
                this.log(search.value, "onClearClicked");
            }
            search.value = "";
            this.ClearSearchEvent.emit();
        };
        /**
         * @param {?} search
         * @param {?} event
         * @return {?}
         */
        SearchComponent.prototype.onSearchKeyUp = /**
         * @param {?} search
         * @param {?} event
         * @return {?}
         */
        function (search, event) {
            if (this.debug) {
                this.log(search.value, "onSearchKeyUp " + event.code + ":" + search.value);
            }
            this.notifySearchEvent(event, search);
            this.KeyUpEvent.emit(search.value);
        };
        /**
         * @param {?} search
         * @return {?}
         */
        SearchComponent.prototype.onSearchClicked = /**
         * @param {?} search
         * @return {?}
         */
        function (search) {
            if (this.debug) {
                this.log(search.value, "onSearchClicked" + search.value);
            }
            if (!search.value) {
                setTimeout((/**
                 * @return {?}
                 */
                function () {
                    search.style.background = "";
                }), 1500);
                search.style.background = "lightPink";
                return;
            }
            this.notifySearch(search.value);
        };
        /**
         * @private
         * @param {?} event
         * @param {?} search
         * @return {?}
         */
        SearchComponent.prototype.notifySearchEvent = /**
         * @private
         * @param {?} event
         * @param {?} search
         * @return {?}
         */
        function (event, search) {
            if (this.debug) {
                this.log(search, "notifySearchEvent" + search.value);
            }
            if (event.code === "Enter") {
                this.notifySearch(search.value);
            }
        };
        /**
         * @private
         * @param {?} search
         * @return {?}
         */
        SearchComponent.prototype.notifySearch = /**
         * @private
         * @param {?} search
         * @return {?}
         */
        function (search) {
            if (this.debug) {
                this.log(search.value, "notifySearch(" + search.value);
                this.SearchEvent.emit(search.value);
            }
        };
        /**
         * @private
         * @param {?} search
         * @param {?} event
         * @return {?}
         */
        SearchComponent.prototype.log = /**
         * @private
         * @param {?} search
         * @param {?} event
         * @return {?}
         */
        function (search, event) {
            if (this.debug) {
                console.log({ SearchTerm: search, Event: event });
            }
        };
        SearchComponent.decorators = [
            { type: core.Component, args: [{
                        selector: "mg-search",
                        template: "<div class=\"searchContainer\">\r\n  <input\r\n    #search\r\n    type=\"text\"\r\n    (keyup)=\"onSearchKeyUp(search, $event)\"\r\n    [placeholder]=\"placeholder\"\r\n  />\r\n  <div class=\"block\">\r\n    <button>\r\n      <fa-icon (click)=\"onSearchClicked(search)\" [icon]=\"faSearch\"></fa-icon>\r\n    </button>\r\n\r\n    <button *ngIf=\"showClearButton\" (click)=\"onClearClicked(search)\">\r\n      Clear\r\n    </button>\r\n  </div>\r\n</div>\r\n",
                        styles: [":host{--fontSize:1rem;--iconColor:white;--inputSize:25em}button{font-size:var(--fontSize);height:1.5em}input{width:var(--inputSize);background:var(--inputBackground);padding-left:.2em}.block{display:grid;grid-template-columns:2em 4em;grid-column-gap:5px;margin-left:.3em}.searchContainer{display:grid;grid-template-columns:17em 10em}fa-icon{background:0 0;color:var(--iconColor)}"]
                    }] }
        ];
        /** @nocollapse */
        SearchComponent.ctorParameters = function () { return []; };
        SearchComponent.propDecorators = {
            debug: [{ type: core.Input, args: ["Debug",] }],
            showClearButton: [{ type: core.Input, args: ["ShowClearButton",] }],
            placeholder: [{ type: core.Input, args: ["placeholder",] }],
            ClearSearchEvent: [{ type: core.Output, args: ["ClearSearchEvent",] }],
            KeyUpEvent: [{ type: core.Output, args: ["KeyUpEvent",] }],
            SearchEvent: [{ type: core.Output, args: ["SearchEvent",] }]
        };
        return SearchComponent;
    }());
    if (false) {
        /** @type {?} */
        SearchComponent.prototype.debug;
        /** @type {?} */
        SearchComponent.prototype.showClearButton;
        /** @type {?} */
        SearchComponent.prototype.placeholder;
        /** @type {?} */
        SearchComponent.prototype.ClearSearchEvent;
        /** @type {?} */
        SearchComponent.prototype.KeyUpEvent;
        /** @type {?} */
        SearchComponent.prototype.SearchEvent;
        /** @type {?} */
        SearchComponent.prototype.faSearch;
    }

Notice the class is just a function with prototypes, the best part is we never see the word prototype while coding.

In Angular we created a SearchComponent, which was a class. Look at what that class became...

// the class was commented out!
// A named function became our class.

var SearchComponent = /** @class */ (function () {
        function SearchComponent() {
            // our variables with initial values
            this.debug = false;
            this.showClearButton = true;
            this.placeholder = "Search";
            // our EventEmitters.
            this.ClearSearchEvent = new core.EventEmitter();
            this.KeyUpEvent = new core.EventEmitter();
            this.SearchEvent = new core.EventEmitter();
            this.faSearch = freeSolidSvgIcons.faSearch;
        }

The Typescript class is a function in the end! Tell that to the 'class haters' of the world! Be careful kid you'll shoot your eye out.

Alt Text

Oh no, prototypes?

 SearchComponent.prototype.ngOnInit = /**
        function () { };

Angular's ngOnInit is just a prototype in our named function. I've had JavaScript experts tell me not to use prototypes. When asked why, it was related to Inheritance. To them, inheritance is a bad thing (unless of course) React uses it.

One more prototype example:

SearchComponent.prototype.onClearClicked = /**
         * @param {?} search
         * @return {?}
         */
        function (search) {
            if (this.debug) {
                this.log(search.value, "onClearClicked");
            }
            search.value = "";
            this.ClearSearchEvent.emit();
        };

Decorators

They are not experimental in Angular, they are how Angular has worked since ESM2015!

 SearchComponent.decorators = [
            { type: core.Component, args: [{
                        selector: "mg-search",
                        template: "<div class=\"searchContainer\">\r\n  <input\r\n    #search\r\n    type=\"text\"\r\n    (keyup)=\"onSearchKeyUp(search, $event)\"\r\n    [placeholder]=\"placeholder\"\r\n  />\r\n  <div class=\"block\">\r\n    <button>\r\n      <fa-icon (click)=\"onSearchClicked(search)\" [icon]=\"faSearch\"></fa-icon>\r\n    </button>\r\n\r\n    <button *ngIf=\"showClearButton\" (click)=\"onClearClicked(search)\">\r\n      Clear\r\n    </button>\r\n  </div>\r\n</div>\r\n",
                        styles: [":host{--fontSize:1rem;--iconColor:white;--inputSize:25em}button{font-size:var(--fontSize);height:1.5em}input{width:var(--inputSize);background:var(--inputBackground);padding-left:.2em}.block{display:grid;grid-template-columns:2em 4em;grid-column-gap:5px;margin-left:.3em}.searchContainer{display:grid;grid-template-columns:17em 10em}fa-icon{background:0 0;color:var(--iconColor)}"]
                    }] }
        ];
        /** @nocollapse */
        SearchComponent.ctorParameters = function () { return []; };
        SearchComponent.propDecorators = {
            debug: [{ type: core.Input, args: ["Debug",] }],
            showClearButton: [{ type: core.Input, args: ["ShowClearButton",] }],
            placeholder: [{ type: core.Input, args: ["placeholder",] }],
            ClearSearchEvent: [{ type: core.Output, args: ["ClearSearchEvent",] }],
            KeyUpEvent: [{ type: core.Output, args: ["KeyUpEvent",] }],
            SearchEvent: [{ type: core.Output, args: ["SearchEvent",] }]
        };
        return SearchComponent;
    }());

And the HTML which is fully contained in the template argument of the Search.Component.Decorators property above.

<div class="searchContainer">
  <input
    #search
    type="text"
    (keyup)="onSearchKeyUp(search, $event)"
    [placeholder]="placeholder"
  />
  <div class="block">
    <button>
      <fa-icon (click)="onSearchClicked(search)" [icon]="faSearch"></fa-icon>
    </button>

    <button *ngIf="showClearButton" (click)="onClearClicked(search)">
      Clear
    </button>
  </div>
</div>

And the Typescript

import { Component, OnInit, Output, EventEmitter, Input } from "@angular/core";
import { faSearch } from "@fortawesome/free-solid-svg-icons";

@Component({
  selector: "mg-search",
  templateUrl: "./search.component.html",
  styleUrls: ["./search.component.css"],
})
export class SearchComponent implements OnInit {
  @Input("Debug") debug: boolean = false;
  @Input("ShowClearButton") showClearButton: boolean = true;
  @Input("placeholder") placeholder: string = "Search";
  @Output("ClearSearchEvent") ClearSearchEvent: EventEmitter<
    null
  > = new EventEmitter();

  @Output("KeyUpEvent") KeyUpEvent: EventEmitter<
    HTMLInputElement
  > = new EventEmitter<HTMLInputElement>();
  @Output("SearchEvent") SearchEvent: EventEmitter<string> = new EventEmitter<
    string
  >();

  faSearch = faSearch;

  constructor() {}

  ngOnInit() {}
  onClearClicked(search) {
    if (this.debug) {
      this.log(search.value, "onClearClicked");
    }
    search.value = "";
    this.ClearSearchEvent.emit();
  }
  onSearchKeyUp(search, event) {
    if (this.debug) {
      this.log(search.value, `onSearchKeyUp ${event.code}:${search.value}`);
    }
    this.notifySearchEvent(event, search);
    this.KeyUpEvent.emit(search.value);
  }

  onSearchClicked(search) {
    if (this.debug) {
      this.log(search.value, `onSearchClicked${search.value}`);
    }
    if (!search.value) {
      setTimeout(() => {
        search.style.background = "";
      }, 1500);
      search.style.background = "lightPink";
      return;
    }

    this.notifySearch(search.value);
  }

  private notifySearchEvent(event: any, search: any) {
    if (this.debug) {
      this.log(search, `notifySearchEvent${search.value}`);
    }
    if (event.code === "Enter") {
      this.notifySearch(search.value);
    }
  }

  private notifySearch(search: any) {
    if (this.debug) {
      this.log(search.value, `notifySearch(${search.value}`);
      this.SearchEvent.emit(search.value);
    }
  }

  private log(search, event) {
    if (this.debug) {
      console.log({ SearchTerm: search, Event: event });
    }
  }
}

Conclusion
I like Typescript better, it's easier to read, and much less verbose. Its pre-runtime type checking saves time. But in the end it doesn't matter.

It's a great tool for strong typers and new coders. Its 50/50 with Javascript experts.

JWP 2020

Posted on by:

jwp profile

John Peters

@jwp

Angular, React, Typescript and JavaScript. The wave of now! Ok Vue too!

Discussion

markdown guide