TypeScript - urfu-2018.github.io · Сегодня Вывод типов Структурная...

Post on 13-Jul-2020

30 views 0 download

Transcript of TypeScript - urfu-2018.github.io · Сегодня Вывод типов Структурная...

TypeScriptЧасть II

 Старков Дима

1

Сегодня

Вывод типов

Структурная типизация

Более сложные типы

Обобщенные типы

Type Guards

2

TypeScript?

Спасет от выстрелов себе в ногу

ESNext прямо сейчас

Средство против TypeError

Пишет код за вас

Документация к коду

Но...3

4

Вывод типов

let n: number = 42 let s: string = 'Hello, world!'

let a: number[] = [1, 2, 3, 4] let n = 42 let s = 'Hello, world!' let a = [1, 2, 3, 4]

5

Наиболее общий тип

let shapes = [new Circle(), new Square()]

shapes.push(new Triangle())

6

Наиболее общий тип

let shapes = [new Circle(), new Square()]

// Argument of type 'Triangle' // is not assignable to parameter of type 'Square | Circle'. shapes.push( )new Triangle()~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

7

Наиболее общий тип

let shapes = [new Circle(), new Square()]

shapes.push(new Triangle())

8

Наиболее общий тип

let shapes = [new Circle(), new Square()]

shapes.push(new Triangle())

9

Наиболее общий тип

let shapes = [new Circle(), new Square()]

shapes.push(new Triangle())

10

Наиболее общий тип

let shapes = [new Circle(), new Square()]

// Argument of type 'Triangle' // is not assignable to parameter of type 'Square | Circle'. shapes.push( )new Triangle()~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

11

Наиболее общий тип

let shapes: Shape[] = [new Circle(), new Square()]

shapes.push(new Triangle())

12

Совместимость типов

class Human { name: string

} class Robot { name: string } let human: Human = new Robot()

13

Проверка типа в runtime

function addShape(shapes: Shape[], obj: object) { if (obj instanceof Shape) {

shapes.push(obj as Shape) } throw new TypeError('Argument is not instanceof Shape') }

14

Проверка типа в runtime

function addShape(shapes: Shape[], obj: object) { if (obj instanceof Shape) {

shapes.push(obj as Shape) } throw new TypeError('Argument is not instanceof Shape') }

15

Проверка типа в runtime

function addShape(shapes: Shape[], obj: object) { if (obj instanceof Shape) {

shapes.push(obj as Shape) } throw new TypeError('Argument is not instanceof Shape') }

16

Проверка типа в runtime

function addShape(shapes: Shape[], obj: object) { if (obj instanceof Shape) {

shapes.push(obj as Shape) } throw new TypeError('Argument is not instanceof Shape') }

17

Проверка типа в runtime

function addShape(shapes: Shape[], obj: object) { if (obj instanceof Shape) {

shapes.push(obj as Shape) } throw new TypeError('Argument is not instanceof Shape') }

18

Type Guard

function addShape(shapes: Shape[], obj: object) { if (obj instanceof Shape) {

shapes.push(obj) } throw new TypeError('Argument is not instanceof Shape') }

19

TypeScript крут. Но можем ли мы описать весь JavaScript?

20

Вспомним TypeScript 1.0

Интерфейсы

Классы

Обобщенные типы

Перегрузки функций

Чего еще желать?

21

// String.split split(separator: ?, limit: number): string[]

22

// String.split split(separator: string | RegExp, limit: number): string[]

23

// String.split split(separator: string | RegExp, limit: number): string[]

 Решение: Union Types

24

Union Type Guard

function negate(n: string | number) { if (typeof n === 'string') {

return '-'.concat(n); } else { return -n; } }

25

Union Type Guard

function negate(n: string | number) { if (typeof n === 'string') {

return '-'.concat(n); } else { return -n; } }

26

Union Type Guard

function negate(n: string | number) { if (typeof n === 'string') {

return '-'.concat(n); } else { return -n; } }

27

Union Type Guard

function negate(n: string | number) { if (typeof n === 'string') {

return '-'.concat(n); } else { return -n; } }

28

Union Type Guard

function negate(n: string | number) { if (typeof n === 'string') {

return '-'.concat(n); } return -n; }

29

Intersection Types

type Cat = { purr()

}

30

Intersection Types

type Cat = { purr()

} type Dog = { woof() }

31

Intersection Types

type Cat = { purr()

} type Dog = { woof() } type CatDog = Cat & Dog

32

Type Alias

// String.split split(separator: string | RegExp, limit: number): string[]

33

Type Alias

type StringOrRegExp = string | RegExp

// String.split split(separator: StringOrRegExp, limit: number): string[]

34

Type vs Interface

type Point = { x: number

y: number }

interface Point { x: number

y: number }

implements interface

Type1 | Type2

35

Тип ≡ Множество

Можем объединять типы |

Можем пересекать типы &

Можем вычитать из одного типа другой

Фух, теперь точно всё...

36

А вот и нет!

function get(obj, keyName) {

return obj[keyName] }

function get(obj: any, keyName: string): any { return obj[keyName] } // TypeError: Cannot read property 'prototype' of null get(null, 'prototype') 37

А вот и нет!

function get(obj, keyName) {

return obj[keyName] }

function get(obj: any, keyName: string): any { return obj[keyName] } // TypeError: Cannot read property 'prototype' of null get(null, 'prototype')

38

А вот и нет!

function get(obj, keyName) {

return obj[keyName] }

function get(obj: any, keyName: string): any { return obj[keyName] } // TypeError: Cannot read property 'prototype' of null get(null, 'prototype')

39

А вот и нет!

function get(obj, keyName) {

return obj[keyName] }

function get(obj: any, keyName: string): any { return obj[keyName] } // TypeError: Cannot read property 'prototype' of null get(null, 'prototype')

40

Нужно уметь обрабатывать значенияразных типов идентичным образом

Кажется нам нужен...

41

42

Обобщенные типы

function identity(arg: any): any { return arg;

}

43

Обобщенные типы

function identity<T>(arg: T): T { return arg;

}

44

Обобщенные функции

function identity<T>(arg: T): T { return arg;

} identity('string') // T is string identity(12131415) // T is number identity([4, 8, 15, 16, 23, 42]) // T is number[]

45

Встроенные обобщенные типы

const fib: Array<number> = [1, 1, 2, 3, 5]

// Argument of type 'string' // is not assignable to parameter of type 'number'. fib.push( ) const map: Map<number, string> = new Map() // Argument of type 'number' // is not assignable to parameter of type 'string'. map.set(1, )

'1'~~~~~~~~~

1~~~

46

Обобщенные интерфейсы

interface IStack<TItem> { push(item: TItem)

pop(): TItem } let numStack: IStack<number> = [1, 2, 3]

47

Обобщенные интерфейсы

interface IStack<number> { push(item: number)

pop(): number } let numStack: IStack<number> = [1, 2, 3]

48

Обобщенные типы

type AsyncResult<TResult> = Promise<TResult> | TResult

let result: AsyncResult<string> = Promise.resolve('200') let result: AsyncResult<string> = '200'

49

Обобщенные типы

type AsyncResult<string> = Promise<string> | string

let result: AsyncResult<string> = Promise.resolve('200') let result: AsyncResult<string> = '200'

50

Обобщенные классы

class Stack<TItem> implements IStack<TItem> { private state: TItem[]

constructor() { this.state = [] } push(item: TItem) { this.state.push(item) } pop(): TItem { return this.state.pop() }

51

Обобщенные классы

class Stack<TItem> implements IStack<TItem> { private state: TItem[] = []

push(item: TItem) { this.state.push(item) } pop(): TItem { return this.state.pop() } }

52

Обобщенные типы

interface ISwim { swim()

} class Dog implements ISwim { swim() { ... } } class Duck implements ISwim { swim() { ... } }

53

Ограничения на обобщенные типы

function swimTogether< T1 implements ISwim,

T2 implements ISwim >(firstPal: T1, secondPal: T2) { firstPal.swim() secondPal.swim() }

54

55

Обобщенные типы

type TypeName<T> = T extends string ? 'string' :

T extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'function' : 'object'

56

Обобщенные типы

type TypeName<string> = string extends string ? 'string' :

T extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'function' : 'object'

57

Обобщенные типы

type TypeName<number> = number extends string ? 'string' :

number extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'function' : 'object'

58

Наша функция

function get(obj: any, keyName: string): any { return obj[keyName]

}

59

Наша функция

function get<T>(obj: T, keyName: string): any { return obj[keyName]

}

60

Хотим знать список полей объекта и их типы на этапе компиляции

Решение: Lookup Types и keyof

61

Lookup типы

interface IUser { login: string

age: number gender: 'male' | 'female' } let login: IUser['login'] let login: string let loginOrAge: IUser['login' | 'age'] let loginOrAge: string | number

62

keyof

interface IUser { login: string

age: number gender: 'male' | 'female' } let key: keyof IUser let key: 'login' | 'age' | 'gender'

63

Наша простая функция

function get(obj, keyName) { return obj[keyName]

}

64

Наша простая функция

function get<T>(obj: T, keyName: keyof T): T[keyof T] { return obj[keyName]

}

65

Наша простая функция

function get<T>(obj: T, keyName: keyof T): T[keyof T] { return obj[keyName]

} let a: number = get({ a: 1 }, 'a')

66

Наша простая функция

function get<{ a: 1 }>(obj: T, keyName: keyof T): T[keyof T] { return obj[keyName]

} let a: number = get({ a: 1 }, 'a')

67

Наша простая функция

function get<{ a: 1 }>(obj: T, keyName: 'a'): T['a'] { return obj[keyName]

} let a: number = get({ a: 1 }, 'a')

68

Наша простая функция

function get<{ a: 1 }>(obj: T, keyName: 'a'): number { return obj[keyName]

} let a: number = get({ a: 1 }, 'a')

69

Наша простая функция

function get<T>(obj: T, keyName: keyof T): T[keyof T] { return obj[keyName]

} let a: number = get({ a: 1 }, 'a') // Argument of type '"c"' // is not assignable to parameter of type '"a" | "b"'. let c: undefined = get({ a: 1, b: 2 }, )

'c'~~~~~~~~~

70

Наша простая функция

function get<T, K extends keyof T>(obj: T, keyName: K): T[K] { return obj[keyName]

} let a: number = get({ a: 1 }, 'a') let c: undefined = get({ a: 1, b: 2 }, 'c')

71

Перерыв

72

А что там в es5?

interface IUser { login: string

age: number gender: 'male' | 'female' } const user = { login: 'dimastark', age: 21, gender: 'male' } const readonlyUser: ? = Object.freeze(user)

73

А что там в es5?

interface IFrozenUser { readonly login: string

readonly age: number readonly gender: 'male' | 'female' } const user = { login: 'dimastark', age: 21, gender: 'male' } const readonlyUser: IFrozenUser = Object.freeze(user)

Решение: Mapped Types

74

75

Mapped Types

interface IUser { login: string age: number

gender: 'male' | 'female' } type Readonly<T> = { readonly [P in 'login' | 'age' | 'gender']: T[P]; }; const user = { login: 'dimastark', age: 21, gender: 'male' } const readonlyUser: Readonly<IUser> = Object.freeze(user)

76

Mapped Types + keyof

interface IUser { login: string age: number

gender: 'male' | 'female' } type Readonly<T> = { readonly [P in keyof T]: T[P]; }; const user = { login: 'dimastark', age: 21, gender: 'male' } const readonlyUser: Readonly<IUser> = Object.freeze(user)

77

infer

type ValueOf<T> = T extends { [key: string]: infer U

} ? U : never; ValueOf<{ a: string, b: string }> // string ValueOf<{ a: string, b: number }> // string | number

78

Mapped Types

interface IUser { login: string birthDate: {

year: number month: number day: number } gender: 'male' | 'female' }

79

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

80

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

81

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

82

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

83

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

84

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

85

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

86

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

87

Mapped Types

type DeepReadonly<T> = { [P in keyof T]:

T[P] extends (infer U)[] ? DeepReadonly<U>[] : T[P] extends object ? DeepReadonly<T[P]> : readonly T[P]; };

88

Ссылочки

TypeScript Handbook. Advanced.

TypeScript Deep Dive

Андрей Старовойт — Эволюция TypeScript

TypeScript Playground

89

Вопросы?

90

Спасибо!

91