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