Slide 1

Slide 1 text

co-IT.eu GmbH Inspire To Change Gregor Woiwode @GregOnNet TypeScript Conditional Types

Slide 2

Slide 2 text

Agenda Syntax Debugging „Error Handling“ Real World Use Case infer nested types *Automated Testing

Slide 3

Slide 3 text

HANDS ON Introducing Conditional Types https://github.com/GregOnNet/conditional-types-playground

Slide 4

Slide 4 text

Conditional types add ternery expressions I N T R O D U C T I O N declare type StringOrNull = T extends string ? string : null;

Slide 5

Slide 5 text

Benefit I N T R O D U C T I O N More flexible type system Better to maintain

Slide 6

Slide 6 text

How to read the syntax I N T R O D U C T I O N declare type StringOrNull = T extends string ? string : null; I f t h e g i v e n t y p e T i s a s t r i n g . . . . . . t h a n re t u r n t h e t y p e s t r i n g . . . o t h e r w i s e re t u r n t h e t y p e n u l l .

Slide 7

Slide 7 text

How to inspect conditional types D E B U G G I N G Since we add logic specifying types more precisely, we need to test it.

Slide 8

Slide 8 text

Fill in the gaps D E B U G G I N G declare type StringOrNull = T extends ... declare type isString = StringOrNull;

Slide 9

Slide 9 text

HANDS ON Manual Debugging

Slide 10

Slide 10 text

Observation D E B U G G I N G declare type isString = StringOrNull; type isStringOrNumber = string | null #

Slide 11

Slide 11 text

Learning D E B U G G I N G $ Unwanted types need to be omitted explicitly.

Slide 12

Slide 12 text

Nested ternary expressions CO N D I T I O N A L T Y P E S declare type StringOrNull = T extends string ? string : T extends null ? null : never; . . . o t h e r w i s e i f T i s o f t y p e n u l l . . . r e t u r n t y p e n u l l . . . o t h e r w i s e re t u r n t y p e n ev e r

Slide 13

Slide 13 text

Error handling for types N E V E R T Y P E % The never type represents the type of values that never occur. https://www.typescriptlang.org/docs/handbook/basic-types.html

Slide 14

Slide 14 text

Examples N E V E R T Y P E function error( message: string ): never { throw new Error(message); } function infiniteLoop(): never { while (true) { } } T h r o wi n g E r r o r s I n fi n i t e Loop

Slide 15

Slide 15 text

HANDS ON Apply fix

Slide 16

Slide 16 text

Congratulations! CO N D I T I O N A L T Y P E S Now, you are familiar with the syntax of conditional types. Now, we will have a look to apply this knowledge in practice.

Slide 17

Slide 17 text

Yet another story about Redux

Slide 18

Slide 18 text

No Redux knowledge needed D I S C L A I M E R No worries, we will continue focusing on conditional types.

Slide 19

Slide 19 text

No Redux bashing D I S C L A I M E R We just have a look at what can be improved. This talk will not mention all the cool benefits coming with Redux.

Slide 20

Slide 20 text

Before centralized state management... I N F E R T Y P E S C O N D I T I O N A L L Y export class RootComponent { notes$: Observable; constructor(private notes: NotesService) { this.notes$ = this.notes.loadNotes(); } }

Slide 21

Slide 21 text

Interacting with the Store is hard I N F E R T Y P E S C O N D I T I O N A L L Y export class RootComponent implements OnInit { constructor(private store: Store) {} ngOnInit() { this.store.dispatch(new LoadNotes()); } } From where do I need to import the state? From where do I need to import the action? Why am I not receiving a return value?

Slide 22

Slide 22 text

Actions need to be maintained I N F E R T Y P E S CO N D I T I O N A L LY export class LoadNotes implements Action { readonly type = NotesActionTypes.LoadNotes; } export class LoadNotesSuccess implements Action { readonly type = NotesActionTypes.LoadNotesSuccess; constructor(public payload: Note[]) {} }

Slide 23

Slide 23 text

An action is process by a reducer function I N F E R T Y P E S C O N D I T I O N A L L Y switch (action.type) { case NotesActionTypes.LoadNotesSuccess: return setNotes(action.payload, slice); ... function setNotes(notes: Note[], slice: NotesSlice): NotesSlice { return { ...slice, entities: notes }; } Case Reducer

Slide 24

Slide 24 text

Wouldn‘t it be nice, if ...? I N F E R T Y P E S C O N D I T I O N A L L Y export class RootComponent implements OnInit { notes$: Observable; constructor(private notesFacade: StoreFacade) { this.notes$ = this.notesFacade.all$; } ngOnInit() { this.notesFacade.loadNotes(); } } Creates Action + Dispatches Action Is created dynamically Offers consumable selectors

Slide 25

Slide 25 text

What needs to be done? I N F E R T Y P E S CO N D I T I O N A L LY Action Type Case Reducer Self Dispatching Action (payload: P) => void | () => void Case Reducer

Slide 26

Slide 26 text

Low hanging fruit | Automating reducer generation I N F E R T Y P E S CO N D I T I O N A L LY Self Dispatching Action Self Dispatching Action Self Dispatching Action Self Dispatching Action Reducer C o m b i n i n g a ct i o n t y p e & c a s e re d uc e r b u i l d i n g t h e red u cer fu n ct i o n .

Slide 27

Slide 27 text

The case reducer challange I N F E R T Y P E S CO N D I T I O N A L LY function caseReducerA(state: T, payload: P): T { ... } function caseReducerB(state: T, payload: Q): T { ""... } function caseReducerC(state: T): T { ""... } Or ... Or ...

Slide 28

Slide 28 text

Self dispatching action I N F E R T Y P E S C O N D I T I O N A L L Y Type Case Reducer

Slide 29

Slide 29 text

HANDS ON Getting to know infer https://github.com/GregOnNet/conditional-types-playground

Slide 30

Slide 30 text

Learning I N F E R T extends (state: any, payload: infer TPayload) "=> any ? (payload: TPayload) "=> void : never; i n f e r l e t s y o u e x t r a c t a t y p e b a s e d o n t h e u s a g e o f t h e r e s p e c t i v e m e t h o d

Slide 31

Slide 31 text

Congratulations! CO N D I T I O N A L T Y P E S Now, you can handle more advanced challenges using conditional types.

Slide 32

Slide 32 text

NgRx Ducks CO N D I T I O N A L T Y P E S @Ducksify({ initialState: ... }) export class NoteDucks { loadAll = effect('Load Notes‘); @Action('Loading Notes succeeded') set(State: State, notes: Note[]) { return { ...state, entities: notes }; } Case Reducer Type @Component({ ... }) export class RootComponent implements OnInit { constructor(@Inject(NoteDucks) private ducks: Duck) {} ngOnInit() { this.ducks.loadAll.dispatch(); } }

Slide 33

Slide 33 text

https://npmjs.org/@co-it/ngrx-ducks

Slide 34

Slide 34 text

Predefined helper types I N F E R T Y P E S C O N D I T I O N A L L Y Exclude Exclude from T those types that are assignable to U. Extract Extract from T those types that are assignable to U. NonNullable Exclude null and undefined from T. ReturnType Obtain the return type of a function type. InstanceType Obtain the instance type of a constructor function type. ConstructorParameters Obtain the parameter types of a constructor function type. https://www.typescriptlang.org/docs/handbook/advanced-types.html

Slide 35

Slide 35 text

Recapitulation THE END Conditional Types are ternary expressions Conditional Types can be nested Nested types can be extracted with infer Conditional Types add more flexibility Automated tests can be done with ts-snippet

Slide 36

Slide 36 text

medium.com/@gregor.woiwode @GregOnNet http://speakerdeck.com/gregonnet/conditional-types

Slide 37

Slide 37 text

Further Reading R E S O U R C E S Conditional types in TypeScript - Artsy Engineering TypeScript 2.8: Conditional Types — Marius Schulz Notes on TypeScript: Conditional Types - DEV Community *+ Conditional types in TypeScript – JavaScript everyday – Medium TypeScript: Create a condition-based subset types – DailyJS – Medium A Look at TypeScript’s Conditional Types TypeScript conditional types real-life example - codewithstyle.info https://www.reddit.com/r/typescript/comments/9n189u/how_are_you_using_conditional_types/