DEV Community

Cover image for Typescript is just annotation!
John Peters
John Peters

Posted on • Updated on

Typescript is just annotation!

Some college professors loved to annotate paperwork. Its purpose was to correct us, and indeed it did, mostly due to the shock factor.

In programming, its power in pre-runtime error checking, intellisense and all the other hats that are hung on annotations is unmatched by plain old JavaScript. It saves us from the shock of finding production level run time errors.

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;
    }

Enter fullscreen mode Exit fullscreen mode

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;
        }
Enter fullscreen mode Exit fullscreen mode

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 () { };
Enter fullscreen mode Exit fullscreen mode

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();
        };
Enter fullscreen mode Exit fullscreen mode

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;
    }());
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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 });
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)