A implementação da função TypeScript rebaixa os tipos de parâmetro para `any` se você declarar um tipo de parâmetro
Eu tenho uma interface de função assim:
interface Callback {
(a: string, b: number): void;
}
E eu posso implementá-lo sem declarar os tipos de parâmetro assim:
const callback: Callback = (a, b) => { }
Neste caso, o TypeScript entende que os tipos de parâmetro de callback são na verdade (a: string, b: number).
No entanto, se eu declará-lo com um parâmetro tipado, como b: number:
const callback: Callback = (a, b: number) => { }
O tipo do outro parâmetro a se torna any. Exemplo no Playground. O que é estranho é que o compilador sabe qual deve ser o tipo de a, porque ele não permite que você o defina de forma errada, por exemplo (a: boolean, b: number) dirá que os parâmetros são incompatíveis. Por que ele não infere o tipo do parâmetro a?
O exemplo acima é trivial, mas está me dando um pouco de dor de cabeça ao tentar gerar um mapa de reducers do Redux com segurança de tipos:
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 };
}
}
Como cada função irá receber um subtipo de IAction como seu parâmetro action (neste caso MyAction), devo declarar seu tipo no parâmetro de callback. Mas, uma vez que declaro seu tipo, perco o tipo de state e preciso declará-lo. Quando tenho dezenas de callbacks e um nome de estado real como DataImportMappingState para cada callback, isso é bastante irritante.
Resposta #1
A inferência de a e b vem da tipagem contextual. Anteriormente, os parâmetros só eram tipados contextualmente se todos os parâmetros não possuíssem anotações.
Esse comportamento mudou na versão mais recente do compilador TypeScript porque parecia ser pouco intuitivo. Agora a tipagem contextual será aplicada a todos os parâmetros que não têm anotações de tipo explícitas.