Platform: Type mismatch with angular 5.2 and typescript 2.6.2

Created on 11 Jan 2018  路  8Comments  路  Source: ngrx/platform

I'm submitting a...


[x] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Feature request
[ ] Documentation issue or request

What is the current behavior?


consider the following snippet

export interface RouterStateUrl {
  url: string;
  queryParams: Params;
  params: Params;
}

export interface RootState {
  router: RouterReducerState<RouterStateUrl>;
}

export const reducers: ActionReducerMap<RootState> = {
  router: routerReducer
};

tsconfig.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "skipLibCheck": true,
    "noUnusedParameters": true,
    "noUnusedLocals": true,
    "strict": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2016",
      "dom"
    ]
  }
}

Do note that strict: true

It worked with typescript 2.5.3, but after updating to typescript 2.6.2 (which is now supported in angular 5.2.0), compiler has show an error and project can not be successfully built anymore

Error:(22, 14) TS2322: Type '{ router: <T = RouterStateSnapshot>(state: RouterReducerState<T>, action: RouterAction<any, Route...' is not assignable to type 'ActionReducerMap<RootState, Action>'.
  Types of property 'router' are incompatible.
    Type '<T = RouterStateSnapshot>(state: RouterReducerState<T>, action: RouterAction<any, RouterStateSnap...' is not assignable to type 'ActionReducer<RouterReducerState<RouterStateUrl>, Action>'.
      Types of parameters 'state' and 'state' are incompatible.
        Type 'RouterReducerState<RouterStateUrl> | undefined' is not assignable to type 'RouterReducerState<RouterStateUrl>'.
          Type 'undefined' is not assignable to type 'RouterReducerState<RouterStateUrl>'.

Expected behavior:


App continue to work with typescript 2.6.2

Minimal reproduction of the problem with instructions:


clone this repo ngrx-bug-report and serve --aot

git clone https://github.com/sandangel/ngrx-bug-report.git

ng serve --aot

Version of affected browser(s),operating system(s), npm, node and ngrx:

Chrome 63, Ubuntu 16.04, npm 5.6, node 8.9.4, ngrx 4.1.1

Other information:

Accepting PRs Router Store bug

Most helpful comment

@brandonroberts @MikeRyanDev this is not only problem of the router, but basically with approach of typed actions.
If you bump angular, install typescript ^2.6 and add "strictFunctionTypes": true, to platform/example-app/tsconfig.app.json.
Then yarn run example:start will not compile and will give a lot of errors.

See: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html

All 8 comments

Confirming, i have this problem, too!
iI you use
"typescript": "~2.5.3" along with angular 5.2 it works again.

seem like typescript 2.6.2 break many other libs, switched back to 2.5.3 now

@sandangel It looks like this error is displaying when strictFunctionTypes is set to true. Can you confirm by setting strict to false and just enabling strictFunctionTypes?

I confirm that it work when set strict to true and strictFunctionTypes to false

@brandonroberts @MikeRyanDev this is not only problem of the router, but basically with approach of typed actions.
If you bump angular, install typescript ^2.6 and add "strictFunctionTypes": true, to platform/example-app/tsconfig.app.json.
Then yarn run example:start will not compile and will give a lot of errors.

See: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html

I confirm that I run into the same problem of type incompatibility, and I also have created an issue (https://github.com/ngrx/platform/issues/611#issuecomment-357678473) that is unanswered.

I don't agree to the fact that we need to set "strictFunctionTypes": false to compile properly.
IMHO that flag unsets a benefit we can get from the compiler... and ngRx should not require that.

I have modified ofType implementation and solved error in effects. Then using REDUCER_TOKEN instead of ActionReducerMapp as a work around.

import { Action } from '@ngrx/store';
import { filter } from 'rxjs/operators';

export function ofType<T extends Action>(...allowedTypes: string[]) {
  return filter((action: Action): action is T => {
    return allowedTypes.some(type => type === action.type);
  });
}
export const REDUCER_TOKEN = new InjectionToken<ActionReducerMap<State>>('Registered Reducers');

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule.withServerTransition({ appId: 'my-app' }),
    BrowserAnimationsModule,
    LayoutModule,
    MaterialModule,
    SharedModule,
    RouterModule.forRoot([]),
    ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }),
    /* use token here */
    StoreModule.forRoot(REDUCER_TOKEN),
    EffectsModule.forRoot([RouterEffects]),
    !environment.production ? StoreDevtoolsModule.instrument() : [],
    StoreRouterConnectingModule.forRoot({
      stateKey: 'router'
    })
  ],
  bootstrap: [AppComponent],
  providers: [
    { provide: RouterStateSerializer, useClass: CustomSerializer },
    // Provide reducer map here
    { provide: REDUCER_TOKEN, useFactory: () => ({ router: routerReducer }) }
  ]
})
export class AppModule {}

Or you can use ActionReducerMap to ignore type checking

hi, I still have this problem and I suppose that this issue was not fixed by #841
The source of error is the contravariantly check in ts 2.6 with ActionReducer type and routerReducer type:

export interface ActionReducer<T, V extends Action = Action> {
    (state: T | undefined, action: V): T;
}
// and
export declare function routerReducer<T = RouterStateSnapshot>(
  state: RouterReducerState<T>, 
  action: RouterAction<any, T>
): RouterReducerState<T>;

Contravariance in the argument type means A <: B implies (B -> T) <: (A -> T) (A and B flipped sides).

which mean:

  • type T | undefined of ActionReducer have to be assignable to type of state parameter in routerReducer
    and
  • action: { type: string } have to be assignable to action: RouterAction

both are not possible since undefined is not assignable to RouterReducerState and string is not assignable to string literal

I suggest to fix this issue we have to change the signature of routerReducer to

export declare function routerReducer<T = RouterStateSnapshot>(
  state: RouterReducerState<T> | undefined, // add undefined
  action: RouterAction<any, T>
): RouterReducerState<T>;

and change the default type in ActionReducerMap<T, V = Action> to ActionReducerMap<T, V = any>;

Was this page helpful?
0 / 5 - 0 ratings

Related issues

smorandi picture smorandi  路  3Comments

brandonroberts picture brandonroberts  路  3Comments

shyamal890 picture shyamal890  路  3Comments

axmad22 picture axmad22  路  3Comments

NathanWalker picture NathanWalker  路  3Comments