Tomasz Ducin
JavaScript + Java
= TypeScript
Tomasz Ducin
22nd April 2017, Warsaw
JavaScript + Java
= TypeScript
Tomasz Ducin
14th February 2017, Warsaw
#11
JavaScript + Java
= TypeScript
Tomasz Ducin
8th February 2017, Warsaw
JavaScript + C#
= TypeScript
Tomasz Ducin
31st January 2017, Warsaw
#196
JavaScript + Java
= TypeScript
Tomasz Ducin
30th September 2016, Warsaw
JavaScript + Java
= TypeScript
Tomasz Ducin
23rd September 2016, Praha
JavaScript + Java
= TypeScript
Tomasz Ducin
8th September 2016, Warsaw
You don't know TS
Tomasz Ducin
15th June 2016, Darmstadt
JavaScript + Java
= TypeScript
JavaScript + Java = TypeScript
Tomasz Ducin
8th June 2016, Warsaw
#22
Tomasz Ducin
23rd April 2016, Szczecin
JavaScript + Java
= TypeScript
Tomasz Ducin
11th April 2016, Warsaw
JavaScript + Java
= TypeScript
Tomasz Ducin
JavaScript, Java, Python
software developer @ Ivanti
trainer @ Bottega IT Solutions
blah, blah, blah...
Agenda
- a glance at TS
- JS tooling landscape
- TypeScript - and tech problems:
solved & unsolved - JS, ES6, TS, CS... & flow
- TS: profit and loss
TypeScript
experienced?
Are you
- private customer banking interface
- TypeScript-based
- 1+ year to go on production
- deployed on 120+ Scandinavian banks
Retail Banking
- massive fake data generator
-
based on:
- JSON Schema
- faker.js, chance.js
- started in 2014
- rewritten to TS in 2016, v0.3
TypeScript
-
typed superset of JavaScript
function objectRange(obj, times) {
...
}
function objectRange(obj: Object, times: number) {
...
}
function objectRange(obj: Object, times: number): Object[] {
...
}
module myModule {
function objectRange(obj: Object, times: number): Object[] {
...
}
...
}
-
new language, transpiled to JS
-
classes, interfaces, modules, generics
- inherited from Java/.Net -
runtime & compile-time
-
maintained by Microsoft & Google
module myModule {
interface Person {
name: string;
age: number;
}
class MyClass {
private varA: typeA;
constructor(varB: typeB) {...}
public objectRange(obj: Object, times: number): Object[] {
...
}
}
...
}
function objectRange<T>(obj: T, times: number): T[] {
var result: T[] = [];
for (var i = 0; i < times; i++) {
result.push(obj);
}
return result;
}
module cash {
export interface Payment {
amount: number;
account: string;
}
}
var payments = objectRange<cash.Payment>({
"amount":12.34,
"account": "1234 5678 90"
}, 5);
console.log(payments);
function objectRange<T>(obj: T, times: number): T[];
module cash {
export interface Payment {
amount: number;
account: string;
}
}
- weak typing
- strong typing
TypeScript
- weak typing
- asynchronous,
single-threaded
- functional
programming
- both clients and servers
- object oriented programming
- class-based
=
- asynchronous,
single-threaded
- functional
programming
- both clients and servers
- prototype-based
- class-based
- synchronous/parallel mainly
- object oriented programming
- server-side
- dynamic typing
- static typing
- static typing
JavaScript
Java
+
JavaScript Fatigue
x
x
x
x
cool kids fighting
for their frameworks
think about the problems you solve,
NOT the tools
~98% aware of TS existence
~72% doesn't know TS / only ~28% does
~53% interested in learning / ~47% not
~85% satisfaction with TS
just what I wanted to say is, please try not introduce new technologies and thereby complexities without having a good reason to do so... For now we have a lot to cope with, So if you guys keep racing then we don't have a chance to get somehow on the same boat.
a colleague from my project
why should I use ?
-
because I've got classes!
-
because I've got types!
-
because I shift some responsibility onto the platform!
the purpose of a programming language is to aid programmers in producing error-free programs
problems TS
solves
1. checks types
test.ts(5,5): error TS2345: Argument of type 'string'
is not assignable to parameter of type 'number'.
type time
!!!
Primitive types
string
number
boolean
var a: boolean;
let f = false;
const t: boolean = true;
var a: number = 5;
let b: number;
const c = 20;
var a: string = 'John';
let name: string = `${a}`;
const c = "training";
array
var boolList: Array<boolean>
let boolList: boolean[]
const cList: boolean[] = [];
cList.push(true);
same
const
as ES6
Function types
function fullName(first, last) {
return first + ' ' + last;
}
function fullName(first, last): string {
return first + ' ' + last;
}
function fullName(first: string, last: string) {
return first + ' ' + last;
}
function fullName(first: string, last: string): string {
return first + ' ' + last;
}
nowhere
return
params
both
type Oper = (a: number, b: number) => number;
const add: Oper = (a, b) => a + b;
const sub: Oper = (a, b) => a - b;
const mul: Oper = (a, b) => a * b;
const div: Oper = (a, b) => a / b;
get(url: string,
data?: Object|string,
success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any,
dataType?: string
): JQueryXHR
priceless for defining callbacks
jQuery.get(...)
type
Function types
interface Person {
first: string;
last: string;
}
interface Person {
first: string; // required
last: string; // required
age?: number; // optional
}
interface Address {
city: string;
country: string;
postcode?: string;
}
interface Person {
first: string; // required
last: string; // required
age?: number; // optional
addresses: Address[];
}
C-style structs
contracts
Interfaces
type Currency =
"EUR" | "GBP" | "USD" | "AUD" | "CAD";
type Season =
"spring" | "summer" | "autumn" | "winter";
typedefs
let currentSeason: Season = "autumn"
currentSeason = "cucumber" // ERROR
String literal types
Tuples
type Currency =
"EUR" | "GBP" | "USD" | "AUD" | "CAD" | "NZD"
type MoneyTuple = [number, string]
let m: MoneyTuple = [4.99, "EUR"]
type MoneyTuple = [number, Currency]
let m1: MoneyTuple = [4.99, "EUR"]
let m2: MoneyTuple = [4.99, "PLN"] // ERROR
non-string enumerables
enum Directions {
Up, Down, Left, Right
}
lets moves: Directions[] = [
Directions.Right
Directions.Up,
Directions.Right
Directions.Left,
Directions.Up,
Directions.Up,
Directions.Up,
Directions.Left,
Directions.Down,
Directions.Up,
Directions.Left,
Directions.Down,
];
C-style enums
Enums
interface IPerson {
firstName: string,
lastName: string
}
type PersonString = string
type PersonTuple = [string, string]
type UberPerson = IPerson
| PersonString
| PersonTuple
function greet(person: UberPerson) {...}
greet("John Lennon")
greet(["John", "Lennon"])
greet(["John", "Lennon", 40]) // ERROR
greet({ firstName: "John", lastName: "Lennon" })
greet({ firstName: "John", lastName: "Lennon", age: 40 }) // ERROR
Unions
Intersections
interface Person {
first: string;
last: string;
age?: number;
}
interface Citizen {
regNo: string;
bankAccount: string;
}
const john: Person & Citizen = {
first: "John",
last: "Lennon",
regNo: "957204578204",
bankAccount: "94 9834 0956 1238 5499"
}
problems TS
solves
3. less unit tests
assert.isString
assert.isNumber
assert.isBoolean
problem exists in all weakly-typed languages
2. less runtime bugs
problems TS
solves
4. self-documenting
interface AccountingSummary {
id: string;
items: AccoutningItem[];
price: Amount;
signedBy?: Employee;
submittedBy?: Employee;
}
class Department {
public generateSummary(...): AccountingSummary {
... // long method
};
...
}
problems TS
solves
5. type inference
let foo = 123;
let bar = "Hello";
let foo = 123; // foo is a `number`
let bar = "Hello"; // bar is a `string`
foo = bar;
// Error: cannot assign
// `string` to a `number`
let foo = 123; // foo is a `number`
let bar = "Hello"; // bar is a `string`
problems TS
solves
6. reduces JS pitfalls
// JS
var a = 4;
var b = 5;
var c = a * b;
// c == 20
// JS
var a = "hello";
var b = "world";
var c = a * b;
// c == NaN
// TS
var a: string = "hello";
var b: string = "world";
var c: string = a * b;
// TS
var a = "hello";
var b = "world";
var c = a * b;
test.ts(4,5): error TS2322: Type 'number' is not assignable to
type 'string'.
test.ts(4,17): error TS2362: The left-hand side of an arithmetic
operation must be of type 'any', 'number' or an enum type.
test.ts(4,21): error TS2363: The right-hand side of an arithmetic
operation must be of type 'any', 'number' or an enum type.
problems TS
solves
7. disables JS hacks
var mod = null;
function logic(){
... use `mod` here
}
module.exports = function(){
mod = mod || require('MODULE');
return logic.apply(...);
}
?
var mod = require('MODULE');
module.exports = function logic(){
... use `mod` here
}
problems TS
solves
8. native modules
module outer {
var str = "hello world";
module inner {
var num = 6;
}
}
var outer;
(function (outer) {
var str = "hello world";
var inner;
(function (inner) {
var num = 6;
})(inner || (inner = {}));
})(outer || (outer = {}));
problems TS
solves
9. big business logic under control
Design Patterns
Domain Driven Design
class ThatDoesSomething {
public mainPromise: ng.IPromise<IUser[]>;
public users: IUser[];
private fetchUsers(): ng.IPromise<IUser[]> {
return UserModel.getUsers(params)
.then((response: IUser[]) => {
this.users = response;
});
}
public $onInit(): void {
this.mainPromise = this.fetchUsers();
}
}
Typed Promises
class UserModel {
public getUsers(params: ...): ng.IPromise<IUser[]> {
return Restangular.get('users');
}
}
var promise = Promise(); // of what ???
promise.then(function(???){
data = ???
})
Low-level Refactoring
CORE
M1
M2
M3
change or extend
var price = 7.99;
var price = {
amount: 7.99,
currency: "PLN"
};
Value Objects
declare type Price = number;
interface Price {
amount: number;
}
interface Price {
amount: number;
currency: string;
}
Communication Contract
Data Transfer Objects
// GET /customers/{id}
interface Customer {
firstName: string;
lastName: string;
age: number;
}
Data Transfer Objects
Data Transfer Objects
// GET /customers
// GET /customers/{id}
interface Customer {
firstName: string;
lastName: string;
age: number;
}
declare type CustomerList = Customer[];
Data Transfer Objects
// GET /customers
// GET /customers/{id}
// GET /customers/{id}/addresses
interface Address {
street: string;
city: string;
country: string;
}
interface Customer {
...
addresses: Address[];
}
Data Transfer Objects
getAsyncData({
url: "/customers/<id>"
method: "GET",
...
}, function(customer: Customer): void {
process(customer.firstName); // ok
process(customer.lastName); // ok
process(customer.name); // ERROR!!!
});
problems TS
doesn't solve
1. mistakes
- logical mistakes
- lack of knowledge
- asynchronous errors
(e.g. race conditions)
problems TS
doesn't solve
2. still need to understand JS
- writing TS without knowing JS == disaster
- what output will TS provide?
- read code in terms of both: OOP and async runtime
problems TS
doesn't solve
3. won't make it faster
- won't make you code faster
- won't make app run faster
- actually, will slow both down just a little bit
- TS might produce more code than pure JS (modules, classes)
problems TS
doesn't solve
4. the any type
- stands for NO TYPE
- very easy to use
- very messy to use
- it's very tempting
- sometimes just can't do without
- will spoil your app if used in big amounts
- just like donuts...
var a: any = ... // EXPLICIT any
// a - INFERRED
// b - IMPLICIT any
function doSomething(a, b){
a.push(b);
}
nothing comes for free
problems TS
introduces
1. automation becomes
more expensive
- need to transpile TS down to JS
- big project = big cost
- blockers included
problems TS
introduces
2. compatibility with
3rd party libs
- DefinitelyTyped
- less popular projects lack .d.ts files
problems TS
introduces
3. your teammates need to learn
- probably you'll teach them
- entry level is quite low
problems TS
introduces
4. debugging less convenient
- browsers don't execute TS
- source and output: different
problems TS
introduces
automation
more expensive
3rd party libs compatibility
teach people
debugging less convenient
writing JavaScript is not trendy anymore
JS
vs
ES6
vs
TS
writing TypeScript is not trendy anymore
choose TypeScript for development of any new project
when is it worth to go TS?
project?
people?
- size, codebase
- domain complexity
- open for changes
- non-dogmatic attitude
technical dogma example
JavaScript shall always remain dynamically typed
all decisions have pros and cons
transpiling JS
pros & cons
- more features
- might be less code
- efficiency
- following trends
- more code complexity
- higher entry-level
for BE & junior devs - IDE support needed
- more setup complex
value of the new features
costs of introducing it
>
?
think about the problems you solve, NOT the tools
always be responsible for the tools you introduce
nothing comes for free
all decisions have pros and cons
THX!
4 Q&A
writing JavaScript is not trendy anymore ;)
ES6
- arrows
- classes
- enhanced object literals
- template strings
- destructuring
- default + rest + spread
- let + const
- iterators + for..of
- generators
- unicode
- modules
- module loaders
- map + set + weakmap + weakset
- proxies
- symbols
- subclassable built-ins
- promises
- math + number + string + array + object APIs
- binary and octal literals
- reflect api
- tail calls
TypeScript
- most of what ES6 has
-
type checks
- templates typecheck
( ongoing) - TS -> ES6 (ongoing)
CoffeeScript syntax
# Assignment:
number = 42
opposite = true
# Conditions:
number = -42 if opposite
# Functions:
square = (x) -> x * x
# Arrays:
list = [1, 2, 3, 4, 5]
var list, number, opposite, square;
number = 42;
opposite = true;
if (opposite) {
number = -42;
}
square = function(x) {
return x * x;
};
list = [1, 2, 3, 4, 5];
CoffeeScript syntax
# Splats:
race = (winner, runners...) ->
print winner, runners
var race,
slice = [].slice;
race = function() {
var runners, winner;
winner = arguments[0],
runners = 2 <= arguments.length ?
slice.call(arguments, 1) : [];
return print(winner, runners);
};
CoffeeScript usecase
class DataWidgetCtrl
fetchData: ->
@$timeout =>
@table.loaded = true
resourceObject = angular.fromJson(@$attrs.resource)
model = @$injector.get(resourceObject.model)
@table.promise = model[resourceObject.method](resourceObject.id).then (res) =>
@setData(res)
@init()
, (res) =>
@getHeader(@def.i18n, @table)
@table.error = res.status
, 0
...
var DataWidgetCtrl;
DataWidgetCtrl = (function() {
DataWidgetCtrl.prototype.fetchData = function() {
return this.$timeout((function(_this) {
return function() {
var model, resourceObject;
_this.table.loaded = true;
resourceObject = angular.fromJson(_this.$attrs.resource);
model = _this.$injector.get(resourceObject.model);
return _this.table.promise = model[resourceObject.method]
(resourceObject.id).then(function(res) {
_this.setData(res);
return _this.init();
}, function(res) {
_this.getHeader(_this.def.i18n, _this.table);
return _this.table.error = res.status;
});
};
})(this), 0);
};
...
return DataWidgetCtrl;
})();