W prezentacji mogą się pojawić braki w polskich znakach, ponglish, błędy składniowe. Wszystkich wrażliwych proszę o opuszczenie show przed zobaczeniem kontentu.
1. Zainstaluj node.js oraz npm
2. Uruchom: npm install typescript -g
3. Włącz ulubione IDE
W celu skompilowaniu kodu wystarczy uruchomić polecenie:
tsc helloWorld.ts
Wygeneruje to plik o takiej samej nazwie ale z rozszerzeniem js, w którym znajdzie się wygenerowany kod.
function helloWorld(userName: string): void {
console.log(`Hello ${userName}`);
}
helloWorld('Kamil');
function helloWorld(userName) {
console.log("Hello " + userName);
}
helloWorld('Kamil');
Podczas kompilacji usuwane są wszystkie elementy ts, niewspierane przez js. Kod można uruchomić za pomocą node.js
{
"compilerOptions": {
"module": "system",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "./built/tsc.js",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
(function Application() {
playDemo();
})();
(function Application() {
playDemo();
})();
function playDemo() {
console.log('Hello world');
}
//# sourceMappingURL=index.js.map
function playDemo() {
console.log('Hello world');
}
{
"compilerOptions": {
"module": "system",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "./build/index.js",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
Demo.ts
tsconfig.json
Application.ts
index.js
parametr | opis |
---|---|
charset | The character set of the input files. |
diagnostics | Show diagnostic information. |
disableSizeLimit | Disable size limitation on JavaScript project. |
newLine | Use the specified end of line sequence to be used when emitting files: "crlf"(windows) or "lf" (unix).” |
noImplicitAny | Raise error on expressions and declarations with an implied any type. |
noImplicitReturns | Report error when not all code paths in function return a value. |
noUnusedLocals | Report errors on unused locals. |
noUnusedParameters | Report errors on unused parameters. |
kamil@lolcio:~$ node ./demo1.ts
/home/kamil/demo1.ts:14
transfer();
^
TypeError: transfer is not a function
at Object.<anonymous> (/home/kamil/demo1.ts:14:1)
at Module._compile (module.js:573:30)
at Object.Module._extensions..js (module.js:584:10)
at Module.load (module.js:507:32)
at tryModuleLoad (module.js:470:12)
at Function.Module._load (module.js:462:3)
at Function.Module.runMain (module.js:609:10)
at startup (bootstrap_node.js:158:16)
at bootstrap_node.js:578:3
let transfer = function() {
console.log('send transfer');
}
// ... many lines of codes
transfer = {
amount: 1.00,
title: 'unfortunate tax..'
}
// ... many lines of codes
transfer();
kamil@lolcio:~$ tsc ./demo1.ts
demo1.ts(7,5): error TS2322: Type '{ amount: number; title: string; }'
is not assignable to type '() => void'.
Object literal may only specify known properties,
and 'amount' does not exist in type '() => void'.
function getWelcomeMessage(): string {
return ``;
}
function playDemo() {
let result = getWelcomeMessage();
result = 1; // error: Type number is not assignable to type string
}
let sentence: string = `Hello, my name is ${ fullName }.`; // 1
let list: number[] = [1, 2, 3]; // 2
let list: Array<number> = [1, 2, 3]; // 3
let tuple: [string, number] = ["hello", 10]; // 4
enum Color {Red, Green, Blue} // 5
let colors: Color[] = [Color.Red, Color.Green];
Typów w ts można używać:
function foo(x:number|string) : string {
return `x = ${x}`;
}
const someX: number = 12;
const resultForNumber: string = foo(someX);
const resultForString: string = foo("2");
console.log(resultForNumber);
console.log(resultForString);
unia typów
const enum Color {
GREEN,
RED,
BLUE = 2*2
}
function playDemo() {
let color: Color = Color.BLUE;
console.log(color);
}
const enum Animal {
DOG = 12,
CAT = 7
}
(function (Color) {
Color[Color["GREEN"] = 0] = "GREEN";
Color[Color["RED"] = 1] = "RED";
Color[Color["BLUE"] = 4] = "BLUE";
})
(Color || (Color = {}));
(function (Animal) {
Animal[Animal["DOG"] = 12] = "DOG";
Animal[Animal["CAT"] = 7] = "CAT";
})(Animal || (Animal = {}));
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
function voidFun(): void {
return null; // ewentualnie undefined
}
let y: void = undefined;
let x: void = voidFun();
console.log(x);
let tupleNumber: [number, string] = [1, 'jakis string'];
tupleNumber[0] = ''; // error
tupleNumber[1] = 1; // error
// dalsze elementy tablicy nie są sprawdzane
tupleNumber[2] = 1;
tupleNumber[3] = 'text';
Oznacza się nim funkcje które nic nie mogą zwrócić.
W przeciwieństwie do void, funkcja nie może
ani posiadać instrukcji return ani się kończy w prawidłowy sposób.
Metody oznaczone jako never mogą w środku zawierać jedynie nieskończone pętle, bądź też wyrzucać wyjątek,
zamiast normalnego wyjścia z metody.
function neverFun(): never {
while (true) {
}
}
function neverFunWithThrow(): never {
console.log('neverFunWithThrow');
throw new Error('this is never enging function');
}
js nie sprawdza liczby parametrów, stąd też nie ma przeciążania metod
parametry opcjonalne oraz domyślne są dostępne również w ES6
TS również dopuszcza użycie rest parameters
function getWelcomeMessage(userName: string = 'noname', formal?: boolean): void {
console.log(`${formal ? 'Welcome mr.' : 'Hello'} ${userName}`);
}
function getWelcomeMessageForMultipleUser(formal: boolean = false, ...users: string[]): void {
console.log(`${formal ? 'Welcome mr.' : 'Hello'} ${users}`);
}
function playDemo() {
getWelcomeMessage('Kamil', true); // Welcome mr. Kamil
getWelcomeMessage('Kamil'); // Welcome mr. Kamil
getWelcomeMessage(); // Hello noname
getWelcomeMessageForMultipleUser(true, 'Kamil', 'Bartek'); // Welcome mr. Kamil,Bartek
}
za pomocą słowa kluczowego type jesteśmy w stanie zrobić alias dla istniejącego typu
używane jako forma dokumentacji
ułatwia pracę w przypadku typów generycznych
działanie bardzo podobne do interfejsów jednak nie moga być implementowane ani rozszerzane
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === "string") {
return n;
}
else {
return n();
}
}
type Alias = { num: number }
interface Interface {
num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;
w js funkcje traktowane są na równi z innymi obiektami
funkcję można przypisywać do zmiennych
przy deklaracji typów można używać funkcji
interface SearchFunc {
foo(source: string, subString: string): boolean;
}
class User {
constructor(
public name: string,
public age: number ) { }
}
let isUserAdult: (user: User) => boolean = user => user.age > 18;
let users: User[] = [
new User('Zenek', 12),
new User('Martin', 26),
new User('Karol', 34)
];
users
.filter(isUserAdult)
.map(user => `${user.name}=${user.age}`)
.forEach((element, index) => console.log(`${index + 1}.${element}`));
// 1.Martin=26
// 2.Karol=34
interface Food {
name: string;
calories: number;
}
function speak(food: Food): void{
console.log("Our " + food.name + " has " + food.calories + " calories.");
}
speak({
name: "ice cream",
calories: 200
});
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
interface SquareConfig {
readonly color?: string;
readonly width?: number;
}
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
return null;
}
let mySquare = createSquare({ colour: "red", width: 100 }); // error: colour is not color
let mySquare = createSquare({ colour: "red", width: 100 } as SquareConfig);
let userAge: number[] = [];
userAge[0] = 27; // ok
userAge['kamil'] = 27; // error
interface IUserMap {
[key: string]: any; // jak pozbyć się any i wstawić typ??
}
let userAge: IUserMap = [];
userAge[0] = 27; // ok
userAge['kamil'] = 27; // ok
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
abstract class User {
private userName: string;
private age: number;
constructor(userName: string, age: number) {
this.userName = userName;
this.age = age;
}
abstract showUserInfo(): void;
}
class Employee extends User {
private salary: number;
constructor(userName: string, age: number, salary: number) {
super(userName, age);
this.salary = salary;
}
showUserInfo(): void {
console.log(this);
}
}
let firstUser: User = new Employee('Kamil', 18, 1200);
firstUser.showUserInfo(); // Employee { userName: 'Kamil', age: 18, salary: 1200
var User = (function () {
function User(userName, age) {
this.userName = userName;
this.age = age;
}
return User;
}());
var Employee = (function (_super) {
__extends(Employee, _super);
function Employee(userName, age, salary) {
var _this = _super.call(this, userName, age) || this;
_this.salary = salary;
return _this;
}
Employee.prototype.showUserInfo = function () {
console.log(this);
};
return Employee;
}(User));
var firstUser = new Employee('Kamil', 18, 1200);
firstUser.showUserInfo(); // Employee { userName: 'Kamil', age: 18, salary: 1200
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
static readonly kind: string = 'Animal';
constructor (readonly theName: string) {
this.name = theName;
}
}
console.log(Octopus.kind);
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error! name is readonly.
let passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
console.log(employee.fullName);
}
Czy warto tego używać?
class User {
constructor(private readonly account: string) {
}
getUserInfoFactory(): () => string {
return function (): string {
return `account: ${this.account}`;
}
}
}
class Transfer {
private userInfo: () => string;
constructor(private readonly user: User) {
this.userInfo = this.user.getUserInfoFactory();
}
makeTransfer() {
console.log(this.userInfo());
}
}
const user: User = new User('11-2222-3333-4444-5555-66');
new Transfer(user).makeTransfer(); // co zostanie wypisane na ekranie?
class User {
constructor(private readonly account: string) {
}
getUserInfoFactory(): () => string {
return (): string => {
return `account: ${this.account}`;
}
}
}
class Transfer {
private userInfo: () => string;
constructor(private readonly user: User) {
this.userInfo = this.user.getUserInfoFactory();
}
makeTransfer() {
console.log(this.userInfo());
}
}
const user: User = new User('11-2222-3333-4444-5555-66');
new Transfer(user).makeTransfer(); // account: 11-2222-3333-4444-5555-66
-zmienna this jest ustawiana w momencie kiedy funkcja jest wywoływana
-arrow pozwala zapamiętać this z miejsca gdzie funkcja był tworzona
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
// OK, because of structural typing
p = new Person();
let x: Named;
// y's inferred type is { name: string; location: string; }
let y = { name: "Alice", location: "Seattle" };
x = y;
listenEvent(
EventType.Mouse,
(e: Event) => console.log((<MouseEvent>e).x + "," + (<MouseEvent>e).y)
);
-TS potrafi weryfikować zgodność obiektów na podstawie zawartości obiektów
interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;
x = y; // okay, y matches structure of x
interface NotEmpty<T> {
data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
x = y; // error, x and y are not compatible
function foo() {}
foo.x = 123;
console.log(Object.getOwnPropertyDescriptor(foo,'x'))
Result:
{
value: 123,
writable: true,
enumerable: true,
configurable: true
}
class User {
public userName: string;
constructor(userName?: string) {
this.userName = userName;
}
@Deprecated
setUserName(userName: string): void {
this.userName = userName;
}
}
new User().setUserName('Kamil'); // Using deprecated API: User.makeTransfer
function Deprecated(target: any, key: string, descriptor: any) {
return {
value: function (...args: any[]) {
console.warn(`Using deprecated API: ${target.constructor.name}.${key}`);
return descriptor.value.apply(this, args);
}
};
}
class User {
@logProperty
public userName: string;
}
const user: User = new User();
user.userName = 'Kamil'; // Set: userName => Kamil
function logProperty(target: any, key: string) {
// property value
let _val = this[key];
// property getter
const getter = function () {
console.log(`Get: ${key} => ${_val}`);
return _val;
};
// property setter
const setter = function (newVal: any) {
console.log(`Set: ${key} => ${newVal}`);
_val = newVal;
};
// Delete property.
if (delete this[key]) {
// Create new property with getter and setter
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
@LogClass('Creating class with parameter:')
class Person {
public name: string;
public surname: string;
constructor(name: string, surname: string) {
this.name = name;
this.surname = surname;
}
}
var p = new Person("remo", "jansen");
function LogClass(message: string): any {
return function logClass(target: any) {
// save a reference to the original constructor
var original = target;
// a utility function to generate instances of a class
function construct(constructor: any, args: any) {
var c: any = function () {
return constructor.apply(this, args);
}
c.prototype = constructor.prototype;
return new c();
}
// the new constructor behaviour
var f: any = function (...args: any[]) {
console.log(message + args);
return construct(original, args);
}
// copy prototype so intanceof operator still works
f.prototype = original.prototype;
return f;// return new constructor (will override original)
}
}
Decorator factory
import { Component, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
import { ModalService } from '../_services/index';
@Component({
moduleId: module.id.toString(),
selector: 'modal',
template: '<ng-content></ng-content>'
})
export class ModalComponent implements OnInit, OnDestroy {
@Input() id: string;
private element: JQuery;
constructor(private modalService: ModalService, private el: ElementRef) {
this.element = $(el.nativeElement);
}
ngOnInit(): void {
this.modalService.add(this);
}
ngOnDestroy(): void {
this.element.remove();
}
}
module Transfer { // moduł wewnętrzny - tożsamy z namespace
export class Account { }
}
let account: Transfer.Account = new Transfer.Account();
// 1
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();
// 2
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();
// 3
namespace Shapes {
export namespace Polygons {
export class Triangle { }
export class Square { }
}
}
import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // Same as 'new Shapes.Polygons.Square()'
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);
}
}
}
// 1
function identity(arg: number): number {
return arg;
}
// 2
function identity(arg: any): any {
return arg;
}
// 3
function identity<T>(arg: T): T {
return arg;
}
// 4
let output = identity<string>("myString"); // type of output will be 'string'
let output = identity("myString"); // type of output will be 'string'
// 1
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
// 2
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // okay
// error: Argument of type 'm' isn't
// assignable to 'a' | 'b' | 'c' | 'd'
getProperty(x, "m");
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
class Animal {
numLegs: number;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag; // typechecks!
many common problems in JavaScript are alleviated by using let, so you should use it instead of var whenever possible.
- dokumentacja TypeScript
Don’t ever use the types Number, String, Boolean, or Object. These types refer to non-primitive boxed objects that are almost never used appropriately in JavaScript code.
- dokumentacja TypeScript
Don’t write overloads that differ by type in only one argument position:
/* WRONG */
interface Moment {
utcOffset(): number;
utcOffset(b: number): Moment;
utcOffset(b: string): Moment;
}
/* OK */
interface Moment {
utcOffset(): number;
utcOffset(b: number|string): Moment;
}
TypeScript chooses the first matching overload when resolving function calls. When an earlier overload is “more general” than a later one, the later one is effectively hidden and cannot be called.
- dokumentacja TypeScript
/* OK */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;
var myElem: HTMLDivElement;
var x = fn(myElem); // x: string, :)
/* WRONG */
declare function fn(x: any): any;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;
var myElem: HTMLDivElement;
var x = fn(myElem); // x: any, wat?
/* OK */
interface Example {
diff(one: string, two?: string, three?: boolean): number;
}
/* WRONG */
interface Example {
diff(one: string): number;
diff(one: string, two: string): number;
diff(one: string, two: string, three: boolean): number;
}
https://www.typescriptlang.org/index.html
Najlepsze źródło informacji:
import {Component, OnInit, EventEmitter} from '@angular/core';
@Component({
selector: 'app-actions',
templateUrl: './actions.component.html',
styleUrls: ['./actions.component.css']
})
export class ActionsComponent implements OnInit {
modalActions = new EventEmitter<any>();
isFormValid = false;
constructor(private refreshHistoryObserver: RefreshHistoryObserver) {
}
}
Prezentacja powinna dać podstawowe informacje potrzebne do zaczęcia zabawy pracy z Angular 5