Trash Can
Trash Can

Reputation: 6814

TypeScript's type inference

Given these types below, I am not sure why the inferred type of executor.get() is different depending on how I invoke console.log.

I've included a link to the TypeScript playground for your convenience.

interface Stage<T, R> {
  consume(element: T): void;
}

function peek<T>(action: (element: T) => void): Stage<T, T> {
  return {
    consume(element: T) {
      action(element);
    }
  };
}

class Executor<T> {
  run<A>(stage1: Stage<T, A>): Executor<A>;
  run<A, B>(stage1: Stage<T, A>, stage2: Stage<A, B>): Executor<B>;
  run(...stages: Stage<any, any>[]): Executor<any> {
    return this;
  }

  get(): T {
    return null as T;
  }
}

If I do peek(element => console.log(element)), then the inferred return type of get() is number which is correct.

const executor = new Executor<number>();

executor.run(
  peek(element => console.log(element))
).get();

But if I do peek(console.log) like below, then the inferred return type of get() is any which I don't understand why. I think because the signature of console.log is console.log(...data: any[]): void, so when I just pass a reference to console.log like below, TypeScript infers that the type of element is any, but I want number, what should I do to make TypeScript infer the correct type?

const executor = new Executor<number>();

executor.run(
  peek(console.log)
).get();

Upvotes: 0

Views: 268

Answers (1)

user3252327
user3252327

Reputation: 627

console.log has the following type log(message?: any, ...optionalParams: any[]): void;. As such peek when called upon console.log will infer that T is of type any (which is the type of the first argument of console.log). To fix this, you need to manually tell peek that you will be expecting a number:

const executor = new Executor<number>();

executor.run(
  peek<number>(console.log)
).get();

Upvotes: 2

Related Questions