Typescript generics for a polymorphic function

85 Views Asked by At

I would like to correctly type the following function:

const noCI = itOrDescribe => (isCI ? itOrDescribe.skip : itOrDescribe)

Here's my first attempt:

const noCI = <T extends Mocha.SuiteFunction | Mocha.TestFunction>(itOrDescribe: T) => (isCI ? itOrDescribe.skip : itOrDescribe)

By 'correctly', I mean that the code should correctly infer that if describe which is of type Mocha.SuiteFunction gets passed in, the return type should be Mocha.SuiteFunction | Mocha.PendingSuiteFunction, and if it gets passed in, the return type should be Mocha.TestFunction | Mocha.PendingTestFunction.

Instead, the type is always inferred as Mocha.PendingSuiteFunction | Mocha.PendingTestFunction, regardless of which type gets passed in. Why?

To put my generics question more generically, I want a function that takes either a type A or a type B, and depending on whether A or B is passed, return a type derived from either A or B.

1

There are 1 best solutions below

1
Alex Wayne On BEST ANSWER

When you don't like the type Typescript infers, you can always just annotate it yourself.

And in this case that looks like: T | T['skip'].

import Mocha, { it, describe } from 'mocha'

declare const isCI: boolean

const noCI = <
  T extends Mocha.SuiteFunction | Mocha.TestFunction
>(itOrDescribe: T): T | T['skip'] => (
  isCI ? itOrDescribe.skip : itOrDescribe
)

const noCiIt = noCI(it)
// Mocha.PendingSuiteFunction | Mocha.PendingTestFunction

const noCiDescribe = noCI(describe)
// Mocha.PendingSuiteFunction | Mocha.PendingTestFunction

noCiDescribe('test', () => {
  noCiIt('test', () => {
    //...
  })
})

See playground