Valentin Kononov
Full Stack Developer, Speaker
«The World’s Most Misunderstood Programming Language Has Become the World’s Most Popular Programming Language»
> '5' - 3
> '5' + 3
> '5' + - '2'
> 5 + {v: 2}
> 5 - {v: 2}
< 2 // Number
< '53' // String
< '5-2' // String
< '5[object Object]' // String
< NaN // NaN Number
> typeof "hello world"
< "string"
> typeof new String('hello world')
< "object"
Typescript
Anders Hejlsberg created:
public ngOnInit(): void {
const movie = {
name: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: any): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
public ngOnInit(): void {
const movie = {
title: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: any): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
interface Movie {
name: string;
}
public ngOnInit(): void {
const movie: Movie = {
name: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: Movie): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
interface Movie {
title: string;
}
public ngOnInit(): void {
const movie: Movie = {
name: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: Movie): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
interface Movie {
name: string;
}
public ngOnInit(): void {
const movie: Movie = {
name: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: Movie): void {
this.toaster.show(`Today's movie is: ${item['name']`);
}
interface Movie {
name: string;
genre: string;
}
getMovieProp(key: keyof Movie, movie: Movie): string {
return movie[key as string]?.toString();
}
private showToast(item: Movie): void {
this.toaster.show(getMoviewProp('name'));
}
// Typescript
export class SampleService {
public multiply(num: number, num2: number): number {
return num * num2;
}
}
// Javascript
"use strict";
Object.defineProperty(exports, "__esModule",
{ value: true });
class SampleService {
multiply(num, num2) {
return num * num2;
}
}
exports.SampleService = SampleService;
const arg1 = 2, arg2 = 3;
const result1 = service.multiply(arg1, arg2);
// result = 6
const arg1 = 2, arg2: any = '3';
const result1 = service.multiply(arg1, arg2);
// result = 6
const arg1 = 2, arg2: any = 'foo';
const result1 = service.multiply(arg1, arg2);
// result = NaN
const arg1 = 2, arg2: any = { name: 'foo' };
const result1 = service.multiply(arg1, arg2);
// result = NaN
const arg1 = 9, arg2: any = '9';
const result1 = service.sum(arg1, arg2);
// result = 99
// usage of ANY
const arg1: any;
function (arg: any) {...}
// usage of literal properties
const obj = getObj();
someFunc(obj['prop']);
multiplyChecked(num: number, num2: number): number {
if (typeof num !== 'number') {
throw Error(`Wrong arg 1: ${typeof num}`)
}
if (typeof num2 !== 'number') {
throw Error(`Wrong arg 2: ${typeof num2}`)
}
return num * num2;
}
@Typed()
public multiplyChecked(num: number, num2: number): number {
return num * num2;
}
export function Typed() {
return (target: Object, propertyName: string,
descriptor: TypedPropertyDescriptor<Function>) => {
const method = descriptor.value;
descriptor.value = function() {
checkTypes(target, propertyName, arguments);
return method.apply(this, arguments);
};
};
}
import 'reflect-metadata';
function checkTypes(
target: Object, propertyName: string, args: any[]): void {
const paramTypes = Reflect.getMetadata(
'design:paramtypes', target, propertyName);
paramTypes.forEach((param, index) => {
const actualType = typeof arguments[index];
const expectedType =
param instanceof Function ? typeof param() : param.name;
if (actualType !== expectedType) {
throw new Error(`Argument: ${index} of function ${propertyName}
has type: ${actualType} different from
expected type: ${expectedType}.`);
}
});
}
__decorate([
src_1.Typed(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number, Number]),
__metadata("design:returntype", Number)
], TypedTestService.prototype, "multiplyChecked", null);
@Typed()
public multiplyChecked(num: number, num2: number): number {
return num * num2;
}
±0.2 ms
overhead for type checks
npm install ts-stronger-types --save
import * as t from 'io-ts'
const User = t.type({
userId: t.number,
name: t.string
})
npm install io-ts
import { validate } from "superstruct-ts-transformer";
type User = {
name: string;
alive: boolean;
};
const obj = validate<User>(
JSON.parse('{ "name": "Me", "alive": true }'));
npm install superstruct-ts-transformer
export class Post {
@IsString()
title: string;
@IsInt()
@Min(0)
@Max(10)
rating: number;
@IsEmail()
email: string;
@IsDate()
createDate: Date;
}
validate(obj)
.then(() => {...})
.catch(err => {...})
validate(obj)
.then(() => {...})
.catch(err => {...})
npm install class-validators --save
Suggestion from friends research
By Valentin Kononov
Slides for conference topic Runtime Type Safety in Typescript. This practical and also philosophical talk about how to make work with TS safer if this is possible at all.