question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Could typeOf narrowing work without explicit generics in TypeScript 2.8?

See original GitHub issue

While #385 did provide a solution for maintaining action type safety through ofType<Action, ActionType>(action) filtering, it’s a bit inconvenient to use and I’m wondering if predefined conditional types in TS 2.8 (https://github.com/Microsoft/TypeScript/pull/21847) would make it possible to narrow down the type from one big union type, without passing in the explicit type.

I’d imagine most people want to infer action types from action creators and not write explicit type definitions, because of how verbose it gets with lots of actions. This is pretty viable now (see https://medium.com/@martin_hotell/improved-redux-type-safety-with-typescript-2-8-2c11a8062575 for example).

At least from the reducer’s point of view, it’s fine to have one big action union type, which can be derived from all action creators with ReturnType<typeof actionCreator>. Narrowing works just as you’d expect, just matching type with string enum or constant.

I kinda expected it to work the same way in my epics as well, but as the troubleshooting manual says, I need to pass in the exact action type to the generic typeOf for the narrowing to work.

For now I’m doing something like this

// src/state/example/example.epics.ts
import { Epic, ofType, combineEpics } from 'redux-observable'
import { switchMap } from 'rxjs/operators/switchMap'
import { ActionAny, RootState } from '../index'
import * as actions from './example.actions'

const exampleFetchAllEpic: Epic<ActionAny, RootState> = (action$, store) =>
  action$.pipe(
    ofType<ActionAny, ReturnType<typeof actions.exampleActionFetchAll>>(
      actions.ExampleActionType.FETCH_ALL
    ),
    switchMap(action => {
      console.log(action.payload.foo)
    })
  )

export const epics = combineEpics(
  exampleFetchAllEpic
)

Now I don’t quite understand why the narrowing requires this pattern and it’s not a huge issue, so sorry if it’s simply not viable, but I feel like the action type should be implicit since ActionAny is already union of all the return types.

Also TS 2.8 isn’t stable release yet so I don’t expect it to be supported either, but just wanted to bring this up in advance.

Edit: After going through other related issues, I realised the conditional types in 2.8 may not solve the issue here. Still, it feels redundant to declare the exact action type and check the type field, so I’m curious to what needs to happen for the TS compiler to understand the narrowing, or if anyone has workarounds for it.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
zsparalcommented, Mar 12, 2018

I was playing around with TS 2.8 and it’s actually possible to write ofType in a way that automatically narrows discriminated union Action types for a single Action (which I think is a pretty common use-case). Here’s the basic implementation:

  1. First we need a quick helper type for the narrowed action type:
// Depending on how strict we want to be it might be a good idea
// to replace `never` with `any` here
export type NarrowedActionType<A, T> = A extends { type: T } ? A : never;

And then the definition of ofType would change to this:

ofType<U extends string>(type: U): ActionsObservable<NarrowedActionType<T, U>>;
ofType<R extends T = T>(first: T["type"], second: T["type"], ...rest: T["type"][]): ActionsObservable<T>;
0reactions
zsparalcommented, Apr 11, 2018

It does work (#459) but it’s a breaking change and it requires TS 2.8 so I don’t think it’ll get merged until more of the ecosystem transitioned to the new version.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Documentation - Narrowing - TypeScript
TypeScript follows possible paths of execution that our programs can take to analyze the most specific possible type of a value at a...
Read more >
Narrowing a return type from a generic, discriminated union in ...
This method is used to narrow a discriminated union type down, and guarantees that the returned object will always be of the particular...
Read more >
Advanced Types - TypeScript
The right side of the instanceof needs to be a constructor function, and TypeScript will narrow down to: the type of the function's...
Read more >
typescript-cheatsheet - GitHub Pages
Tuple types allow you to express an array where the type of a fixed number ... In TypeScript any argument of a function...
Read more >
The Definitive TypeScript 4.8 Guide - SitePen
When writing an expression (function call, arithmetic operation, etc.), you can also explicitly indicate the resulting type of the expression ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found