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
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}`);
}
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
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]?.toString();
}
private showToast(item: Movie): void {
this.toaster.show(getMoviewProp('name'));
}
export function* iterableObj() {
yield 1;
yield 'am';
yield 'iterable.';
}
export function* iterableObj():
Generator<number, void, void> {
yield 1;
yield 'am';
yield 'iterable.';
}
import { select } from 'redux-saga/effects';
// returns ANY object, no checks
const state_0 = yield select(getTaskStatus);
// if Type is specified - TS relyes on it
const state_1: TaskStatus = yield select(getTaskStatus);
// but Type can be specified incorrectly
const state_2: TaskStatus = yield select(getTaskAssignee);
import { select } from 'redux-saga/effects';
// returns ANY object, no checks
export function select
<Fn extends (state: any, ...args: any[]) => any>(
selector: Fn,
...args: Tail<Parameters<Fn>>
): SelectEffect
// pathOr from Ramda library
export function pathOr<T>(
defaultValue: T, path: Path, obj: any): T;
const role = pathOr(null, ['auth', 'user', 'role'], state);
// no compilation errors
const role = pathOr(null, ['auth', 'userrrr', 'role'], state);
export function pathOr<T1, T2, T3, R>(
defaultValue: R,
path: [keyof T1, keyof T2, keyof T3],
obj: T1): R;
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
....
'@typescript-eslint/no-explicit-any'
'@typescript-eslint/no-empty-function'
'@typescript-eslint/explicit-function-return-type'
'@typescript-eslint/no-use-before-define'
'@typescript-eslint/no-var-requires'
'@typescript-eslint/ban-ts-comment'
'prefer-const'
// 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;
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;
}
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
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.