Aaron Aaron - 9 months ago 41
TypeScript Question

TypeScript function implementation downgrades parameter types to `any` if you declare a param type

I have a function interface like this:

interface Callback {
(a: string, b: number): void;

And I can implement it without declaring parameter types like this:

const callback: Callback = (a, b) => { }

In this case TypeScript understands the parameter types of
are actually
(a: string, b: number)

However, if I declare it with one parameter typed, such as
b: number

const callback: Callback = (a, b: number) => { }

The other parameter
's type becomes
. Example in the Playground. What's odd is that the compiler does know what type
should be, because it won't let you define it wrongly, for example
(a: boolean, b: number)
will say the parameters are incompatible. Why won't it infer the parameter type of

The above is a trivial example, but it's giving me some headache when trying to generate a type-safe Redux reducer map:

interface IReducer<TState> {
(state: TState, action: IAction): TState;

interface IReducerMap<TState> {
[actionType: string]: IReducer<TState>;

interface MyState { hello: string; }

interface MyAction extends IAction { say: string; }

const myReducerMap: IReducerMap<MyState> = {
// Result: `(state: MyState, action: IAction) => MyState`
// But I get an error on `action.say` not defined in `IAction`
reducer1: (state, action) => {
return { hello: action.say };

// Result: `(state: any, action: MyAction) => computed`
reducer2: (state, action: MyAction) => {
return { hello: action.say + state.this_should_be_an_error };

// Works but relies on you to correctly defining state
reducer3: (state: MyState, action: MyAction) => {
return { hello: action.say };

Since each function will take a subtype of
as its
parameter (in this case
), I must declare its type in the callback parameter. But once I declare its type, I lost the type of
and I must declare it. When I have dozens of callbacks and a real state name like
for every callback, this is quite annoying.

Answer Source

The inference of a and b comes from contextual typing. Previously, parameters were only contextually typed if all parameters were unannotated.

This behavior has changed in the latest build of the TypeScript compiler because it seemed to be unintuitive. Now contextual typing will apply to all parameters which don't have explicit type annotations.