TypeScript - ttulka/programming GitHub Wiki
- TypeScript is both a language and a set of tools to generate JavaScript.
- TypeScript compiler has a parameter that can switch between different versions of the ECMAScript standard.
- Definition files for bringing typing into popular JavaScript libraries.
Benefits:
- Compiling.
- Strong or static typing.
- Encapsulation.
- Private and public member variable decorators.
class CountClass {
private _count: number;
constructor() {
this._count = 0;
}
countUp() {
this._count ++;
}
get count() {
return this._count;
}
}
var countInstance = new CountClass() ;
countInstance.countUp();
countInstance.countUp();
console.log(countInstance.count); // 2
function doCalculation(
a : number,
b : number,
c : number) {
return ( a * b ) + c;
}
var result = doCalculation(3,2,1);
var arrayOfNumbers: number [] = [1,2,3];
arrayOfNumbers = [3,4,5,6,7,8,9];
TypeScript will infer the type of a variable based on its first usage, and then assume the same type for this variable in the rest of your code block.
var inferredString = "this is a string";
var inferredNumber = 1;
inferredString = inferredNumber; // error TS2011: Build: Cannot convert 'string' to 'number'
var arrayOfNumbers = [1,2,3];
arrayOfNumbers = [3,4,5,6,7,8,9];
arrayOfNumbers = ["1", "2", "3"]; // error TS2322: Type 'string[]' is not assignable to type 'number[]'
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // error TS2322: Type '(b: number, s: string) => number' is not assignable to type '(a: number) => number'.
class Animal {
feet: number;
constructor(name: string, numFeet: number) { }
}
class Size {
feet: number;
constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s; // OK
s = a; // OK
- The second assignment is an error, because
y
has a required second parameter thatx
does not have, so the assignment is disallowed.
If it looks like a duck, and quacks like a duck, then it probably is a duck.
- Inferred typing for more complex types.
var complexType = { name: "myName", id: 1 };
complexType = { id: 2, name: "anotherName" };
complexType = { id: "string" }; // error TS2322: Type '{ id: string; }' is not assignable to type '{ name: string; id: number; }'
function addNumbers(a: number, b: number) : string {
return a + b;
}
var addResult = addNumbers(2,3); // error TS2322: Type 'number' is not assignable to type 'string'
// fixed
function addNumbers(a: number, b: number) : string {
return (a + b).toString();
}
function doSomethingWithACallback(
initialText: string,
callback : (initialText: string) => string
) {
console.log('callback result: ', callback(initialText));
}
doSomethingWithACallback("abc", (text) => text.toUpperCase()); // callback result: ABC
Specifying that an object has a type of any, in essence, relaxes the compiler's strict type checking.
let list: any[] = [1, true, "free"];
list[1] = "test"; // okay
- Don’t use the return type
any
for callbacks whose value will be ignored (usevoid
instead).
Specifying the absence of having any type at all.
function warnUser(): void {
alert("This is my warning message");
}
var unionType : string | number;
unionType = 1;
unionType = "test";
function addWithUnion(
arg1 : string | number,
arg2 : string | number
) {
return arg1 + arg2; // error TS2365: Operator '+' cannot be applied to types 'string | number' and 'string | number'.
}
type StringOrNumber = string | number;
function addWithAlias(
arg1 : StringOrNumber,
arg2 : StringOrNumber
) {
return arg1.toString() + arg2.toString();
}
type CallbackWithString = (string) => void;
function usingCallbackWithString(callback: CallbackWithString) {
callback("this is a string");
}
enum DoorState {
Open, Closed
}
var doorStatus = DoorState.Open;
console.log(`doorStatus is: ${doorStatus}`); // doorStatus is: 0
doorStatus = DoorState.Closed;
console.log(`doorStatus is: ${DoorState[doorStatus]}`); // doorStatus is: Closed
Enums are compatible with numbers, and numbers are compatible with enums.
enum Status { Ready, Waiting };
let status = Status.Ready;
let num = 0;
status = num; // OKAY
num = status; // OKAY
let x: [string, number];
x = ["hello", 10];
function add(a: string, b: string) : string;
function add(a: number, b:number) : number;
function add(a: any, b: any): any {
return a + b;
}
console.log(add(1, 1)); // 2
console.log(add("1", "1")); // 11
- Sort overloads by putting the more general signatures after more specific signatures.
- Don’t write several overloads that differ only in trailing parameters (use optional parameters
?
instead). - Don’t write overloads that differ by type in only one argument position (use union types
|
instead).
interface IComplexType {
id: number;
name: string;
print() : void;
}
var complexType : IComplexType = {
id: 1,
name: "test",
print(){ console.log(this.id + ": " + this.name); }
};
interface IComplexType {
print() : void;
}
class ComplexType implements IComplexType {
id: number;
name: string;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
print(){ console.log(this.id + ": " + this.name); }
}
var complexType : IComplexType = new ComplexType(1, "test");
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = (source, subString) => {
return source.search(subString) > -1;
}
When an interface type extends a class type it inherits the members of the class but not their implementations.
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
Add additional members to an interface with another interface declaration:
interface Foo {
x: number;
}
// ... elsewhere ...
interface Foo {
y: number;
}
let a: Foo = ...;
console.log(a.x + a.y); // OK
The compiler will not protect you from forgetting to actually add the properties you promised:
interface Foo {
bar: number;
bas: string;
}
var foo = {} as Foo;
The compiler will provide autocomplete for properties of Foo
:
interface Foo {
bar: number;
bas: string;
}
var foo1 = <Foo>{ };
var foo1: Foo = { };
accessible on | public |
protected |
private |
---|---|---|---|
class | yes | yes | yes |
class children | yes | yes | no |
class instances | yes | no | no |
-
public
by default
class ClassWithPrivateProperty {
private id: number;
constructor(id : number) {
this.id = id;
}
}
let privateAccess = new ClassWithPrivateProperty(10);
privateAccess.id = 20; // error TS2341: Property 'id' is private and only accessible within class
- This shorthand syntax is available only within the constructor function.
class ClassWithPrivateProperty {
constructor(private id: number, public name: string) { }
}
let privateAccess = new ClassWithPrivateProperty(1, "test");
privateAccess.name = "test2"; // okay
privateAccess.id = 2; // error TS2341: Property 'id' is private and only accessible within class
class ClassWithReadOnly {
readonly name: string;
constructor(name : string) {
this.name = name;
}
}
var readOnly = new ClassWithReadOnly("test");
readOnly.name = "test2"; // error TS2540: Cannot assign to 'name' because it is a constant or a read-only property
readonly
vs const
: Variables use const whereas properties use readonly.
class StaticProperty {
static count = 0;
static updateCount() {
StaticProperty.count ++;
}
}
StaticProperty.updateCount();
console.log(StaticProperty.count);
class StaticProperty {
public static readonly MAGIC_CONST : number = 42;
}
console.log(StaticProperty.MAGIC_CONST); // 42
abstract class AbstractEmployee {
public id: number;
abstract getDetails(): string;
}
class NewEmployee extends AbstractEmployee {
public name: string;
getDetails(): string { return `id : ${this.id}, name : ${this.name}`; }
}
var employee: AbstractEmployee = new NewEmployee();
interface IBase {
id: number;
}
interface IDerivedFromBase extends IBase {
name: string;
}
class InterfaceInheritanceClass implements IDerivedFromBase {
id: number;
name: string;
}
class BaseClass implements IBase {
id: number;
}
class DerivedFromBaseClass extends BaseClass implements IDerivedFromBase {
name: string;
}
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named = new Person(); // OK, because of type compatibility
namespace MyNameSpace {
class NotExported {
}
export class NameSpaceClass {
id: number;
}
}
var obj = new MyNameSpace.NameSpaceClass();
obj.id = 1;
Validation.ts
:
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
} }
LettersOnlyValidator.ts
:
/// <reference path="Validation.ts" />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
} } }
var list: Array<number> = [1, 2, 3];
class Concatenator<T> {
concatenateArray(inputArray: Array<T>): string {
let returnString = "";
for (let i = 0; i < inputArray.length; i++) {
if (i > 0) returnString += ",";
returnString += inputArray[i].toString();
}
return returnString;
}
}
var stringConcat = new Concatenator<string>();
var stringArray: string[] = ["first", "second", "third"];
var stringResult = stringConcat.concatenateArray(stringArray);
class FootballClubPrinter< T extends IFootballClub > implements IFootballClubPrinter< T > {
// ...
}
- Experimental feature of the TypeScript compiler.
- Proposed as part of the ECMAScript 7 standard.
function SimpleDecorator(constructor : Function) {
console.log('simpleDecorator called.');
}
@SimpleDecorator
class ClassWithSimpleDecorator {
}
function MethodDec(target: any, methodName: string, descriptor?: PropertyDescriptor) {
console.log(`target: ${target}`);
console.log(`methodName : ${methodName}`);
console.log(`target[methodName] : ${target[methodName]}`);
}
class ClassWithMethodDec {
@MethodDec
print(output: string) {
console.log(`ClassWithMethodDec.print` + `(${output}) called.`);
}
}
Allow to use external and third-party JavaScript libraries within TypeScript code.
- Generate a declaration file for a TypeScript code:
tsc test.ts -d
- Install a declaration via npm:
npm install @types/jquery
globals.d.ts
:
declare var MY_GLOBAL_VARS: string [];
test.d.ts
:
///<reference path="globals.d.ts"/>
class GlobalLogger {
static logGlobalsToConsole() {
for(let global of MY_GLOBAL_VARS) {
console.log(`found global var: ${global}`);
}
}
}
window.onload = () => {
GlobalLogger.logGlobalsToConsole();
}
index.html
:
<script src="test.js"></script>
<script>
var MY_GLOBAL_VARS = ["abc", 123];
</script>
declare function trace(arg: string | number | boolean );
declare function trace(arg: { id: number; name: string });
- Declare functions and classes as a single unit.
- At a collision will be merged automatically by the compiler as if they were a single definition.
ErrorHelper.d.ts
:
declare module ErrorHelper {
function containsErrors(response);
function trace(message);
}
test.ts
:
// ...
if (ErrorHelper.containsErrors(message))
ErrorHelper.trace(message);
declare module FirstNamespace {
module SecondNamespace {
module ThirdNamespace {
function log(msg: string);
}
}
}
// can be called as FirstNamespace.SecondNamespace.ThirdNamespace.log("test");
Module is a separate TypeScript file that exposes classes, interfaces, or functions for reuse in other parts of the project.
lib/Module1.ts
:
export class Module1 {
print() {
print(`Module1.print()`);
}
}
function print(functionName: string) {
console.log(`print() called with ${functionName}`);
}
import {Module1} from './lib/Module1';
let mod1 = new Module1();
mod1.print(); // print() called with Module1.print()
lib/Module1.ts
:
var myVariable = "This is a variable.";
export { myVariable }
import { myVariable } from './lib/Module1';
console.log(myVariable);
import {Module1 as m1} from './lib/Module1';
let mod1 = new m1();
mod1.print();
export class Module1 {
print() {
print(`Module1.print()`);
}
}
export {Module1 as NewModule};
We can mark a single item as a default export.
lib/Module2.ts
:
export default class Module2Default {
print() {
console.log(`Module2Default.print()`);
}
}
export class Module2NonDefault {
print() {
console.log(`Module2NonDefault.print()`);
}
}
import M2 from './lib/Module2';
let m2default = new M2();
m2default.print();
foo.d.ts
:
export var SomeVar: { a: SomeType };
export interface SomeType {
count: number;
}
import * as foo from './foo';
let x: foo.SomeType = foo.SomeVar.a;
console.log(x.count);
- Asynchronous Module Definition
npm install requirejs --save
npm install @types/requirejs --save
RequireConfig.js
:
require.config( {
baseUrl: "."
});
index.html
:
<script type="text/javascript"
src="./node_modules/requirejs/require.js" data-main="./RequireConfig" >
</script>
tsconfig.json
:
{ "compilerOptions": {
"module": "amd",
...
lib/Module3.ts
:
export class Module3 {
print() { console.log(`Module3.print()`); }
}
var m3 = require('./Module3');
var module3 = new m3.Module3();
module3.print();
https://www.typescriptlang.org/docs/handbook/declaration-files/templates.html
TODO
- Nathan Rozentals: Mastering TypeScript
- https://www.typescriptlang.org/docs
- https://legacy.gitbook.com/book/basarat/typescript