Sanborn
Sanborn

Reputation: 289

Define TypeScript callback type and provide default callback value

I am having trouble providing a default callback value AND tracking the return type of that callback

In the following case, a and b both have type any but I'd like them to have the return type of whatever callback returns. In this case string and number but in principle, it could be anything

const defaultCallback = () => 'Hello World'

function main(callback: () => any = defaultCallback){
  return callback()
}

const a = main(() => 'Foo') <-- Type is any but should be string
const b = main(() => 1000) <-- Type is any but should be number
const c = main() <-- Type is any but should be string

So to solve for that I've tried a number of things including introducing generics in the following way. This definition works correctly except that I don't seem to be able to provide a default value anymore.

const defaultCallback = () => 'Hello World'

function main<T extends any>(callback: () => T = defaultCallback): T {
  return callback()
}

const a = main(() => 'Hello World') <--- Type string 
const b = main(() => 1000) <--- Type number
const c = main() <-- Should be acceptable which requires a default value for callback

The above code works without defaultCallback but with defaultCallback I receive:

Type '() => string' is not assignable to type '() => T'. Type 'string' is not assignable to type 'T'. 'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'unknown'.(2322)

Upvotes: 2

Views: 621

Answers (1)

You can achieve it with little help of overloadings:

const defaultCallback = () => 'Hello World'

function main<R>(callback: () => R): R
function main(): ReturnType<typeof defaultCallback>
function main(callback = defaultCallback) {
    return callback()
}

const a = main(() => 'Foo')    // string
const b = main(() => 1000)     // number
const c = main()               // string

Playground

Upvotes: 2

Related Questions