DEV Community

yw662
yw662

Posted on

2 2

A "function type covariance" trap in typescript

In JavaScript and TypeScript, functions are generic, which means a:

type f = (...args: [number]) => unknown
// aka: (foo: number) => unknown
Enter fullscreen mode Exit fullscreen mode

is automatically a

type f = (...args: [number, ...any[]]) => unknown
Enter fullscreen mode Exit fullscreen mode

Reasonable. If a function uses only the first few arguments, it is no harm to provide more.

And here come "optional parameters" in TypeScript. No worry in JavaScript since there are no "non-optional parameters":

type g = (foo: number, bar?: number) => unknown
Enter fullscreen mode Exit fullscreen mode

It is also a:

(foo: number) => unknown
Enter fullscreen mode Exit fullscreen mode

Why not? the second parameter is optional, it can be used like that.

So now, a g is also an f.
But wait, remember we have the second form of f:

const H = (h: (foo: number, bar: string) => void) => {
  h(0, '')
}

const F = (f: (foo: number) => void) => {
  H(f)
}

const g = (foo: number, bar?: number) => {
  console.log(bar ?? 0 + foo + 1)
}

F(g)
Enter fullscreen mode Exit fullscreen mode

TypeScript would gladly accept these code even in its most strict type checks, including strictFunctionTypes: a g is an f, we already know that, and an f is an h, we know that too. But is a g also an h ?

That is the question.

We have been using a lot of functional APIs. Array.prototype.map for example, accepts an executor (element, index?, array?) => any, which is practically an element => any.
But if the executor is from somewhere else in the later form, the "g is not h" can be a problem, a problem TypeScript unable to detect:

class Foo<T> {
  private foo: T[]
  ...
  function bar<U>(f: T => U) {
    return this.foo.map(f)
  }
  ...
}
Enter fullscreen mode Exit fullscreen mode

Let's imagine what could happen here.

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay