Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Conditional-Types

 Conditional-Types

Static typing means to curse and blessing alike. They provide with TypeScript robust APIs that are initially rigid in nature. As soon as the use case deviates somewhat, there is a tendency to duplicate large parts of the source code to correspond to the typing.

But you can save yourself this effort. Using conditional types, you create dynamic APIs that are still strongly typed. Your options in programming in TypeScript expand immensely by the knowledge of these language features.

After this talk, you can read, understand and use conditional types.

You will get an introduction to the syntax and the possibilities provided by TypeScript.

Afterwards, you will get an insight into a real project that illustrates the added value in practice.

Talk given at https://www.meetup.com/de-DE/Angular-Munich/events/260848822/

Gregor Woiwode

May 15, 2019
Tweet

More Decks by Gregor Woiwode

Other Decks in Programming

Transcript

  1. Conditional types add ternery expressions I N T R O

    D U C T I O N declare type StringOrNull<T> = T extends string ? string : null;
  2. Benefit I N T R O D U C T

    I O N More flexible type system Better to maintain
  3. How to read the syntax I N T R O

    D U C T I O N declare type StringOrNull<T> = 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 .
  4. 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.
  5. Fill in the gaps D E B U G G

    I N G declare type StringOrNull<T> = T extends ... declare type isString = StringOrNull<string>;
  6. Observation D E B U G G I N G

    declare type isString = StringOrNull<string | number>; type isStringOrNumber = string | null #
  7. Learning D E B U G G I N G

    $ Unwanted types need to be omitted explicitly.
  8. Nested ternary expressions CO N D I T I O

    N A L T Y P E S declare type StringOrNull<T> = 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
  9. 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
  10. 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
  11. 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.
  12. No Redux knowledge needed D I S C L A

    I M E R No worries, we will continue focusing on conditional types.
  13. 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.
  14. 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<Notes[]>; constructor(private notes: NotesService) { this.notes$ = this.notes.loadNotes(); } }
  15. 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<fromNotes.NotesState>) {} 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?
  16. 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[]) {} }
  17. 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
  18. 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<Note[]>; constructor(private notesFacade: StoreFacade) { this.notes$ = this.notesFacade.all$; } ngOnInit() { this.notesFacade.loadNotes(); } } Creates Action + Dispatches Action Is created dynamically Offers consumable selectors
  19. 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
  20. 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 .
  21. 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<T, P>(state: T, payload: P): T { ... } function caseReducerB<T, Q>(state: T, payload: Q): T { ""... } function caseReducerC<T>(state: T): T { ""... } Or ... Or ...
  22. 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
  23. 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
  24. 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.
  25. 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<NoteDucks>) {} ngOnInit() { this.ducks.loadAll.dispatch(); } }
  26. 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<T, U> Exclude from T those types that are assignable to U. Extract<T, U> Extract from T those types that are assignable to U. NonNullable<T> Exclude null and undefined from T. ReturnType<T> Obtain the return type of a function type. InstanceType<T> Obtain the instance type of a constructor function type. ConstructorParameters<T> Obtain the parameter types of a constructor function type. https://www.typescriptlang.org/docs/handbook/advanced-types.html
  27. 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
  28. 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/