Outline serve as overload varieties with TypeScript

Outline serve as overload varieties with TypeScript

[ad_1]

Permit me to briefly solution to the “customary” use case of “The right way to outline serve as
overload varieties with TypeScript” with an instance:

I need a serve as that accepts a callback or returns a promise if none is
supplied:

const logResult = consequence => console.log(`consequence: ${consequence}`)
asyncAdd(1, 2).then(logResult) // logs "consequence: 3"
asyncAdd(3, 6, logResult) // logs "consequence: 9"

Here is how you’ll put into effect this API the use of common JavaScript:

serve as asyncAdd(a, b, cb) {
  const consequence = a + b
  if (cb) go back cb(consequence)
  else go back Promise.unravel(consequence)
}

With this implementation, what we wish is to have TypeScript catch this dangerous
utilization:

// @ts-expect-error as a result of when the cb is supplied, void is returned so you'll be able to't use ".then"!
asyncAdd(1, 2, logResult).then(logResult) // this could throw an error when making an attempt to make use of ".then" (except for we are the use of TypeScript so it may not even bring together 😉)

So, here is how you’ll sort this type of overloading:

sort asyncAddCb = (consequence: quantity) => void
// outline all legitimate serve as signatures
serve as asyncAdd(a: quantity, b: quantity): Promise<quantity>
serve as asyncAdd(a: quantity, b: quantity, cb: asyncAddCb): void

// outline the real implementation
// realize cb is not obligatory
// additionally realize that the go back sort is inferred, but it surely might be specified as `void | Promise<quantity>`
serve as asyncAdd(a: quantity, b: quantity, cb?: asyncAddCb) {
  const consequence = a + b
  if (cb) go back cb(consequence)
  else go back Promise.unravel(consequence)
}

After which you might be off to the races!

The true inspiration for this weblog submit is a little more sophisticated although and
calls for some background knowledge:

I’ve a package deal referred to as
babel-plugin-codegen
that lets you generate code at bring together time. As an example, think you’ve a
document with the next code:

// @codegen
const fs = require('fs')
const end result = fs.readFileSync('./fruit.txt', 'utf8').toString().cut up('n')
module.exports = end result
  .map(fruit => `export const ${fruit} = '${fruit}';`)
  .sign up for('')

Assuming fruit.txt accommodates an inventory of end result, that is what that’ll bring together to:

export const apple = 'apple'
export const orange = 'orange'
export const pear = 'pear'

So, you generate a string of code, and codegen turns that to precise code that is
fed into your output. This unlocks numerous truly cool issues.

However the specifics of this babel plugin does not topic. What does topic is that
you’ll be able to additionally use this with
babel-plugin-macros which
is principally importable babel transforms. So as a substitute of configuring
babel-plugin-codegen, you’ll be able to simply configure babel-plugin-macros after which
set up codegen.macro and also you
can import and use it like so:

import codegen from 'codegen.macro'

// the use of as a tagged template literal:
codegen`
  module.exports = "const tag = 'that is an instance'"
`

// the use of as a serve as
codegen(`
  module.exports = "const fn = 'that is any other instance'"
`)

// codegen-ing an exterior module (and cross an issue):
const jpgs = codegen.require('./get-files-list', '**/*.jpg')

const ui = <Codegen>{`module.exports = require('./some-jsx-code')`}</Codegen>

Then that would bring together to one thing like this:

// the use of as a tagged template literal:
const tag = 'that is an instance'

// the use of as a serve as
const fn = 'that is any other instance'

// codegen-ing an exterior module (and cross an issue):
const jpgs = ['kody.jpg', 'olivia.jpg', 'marty.jpg']

const ui = <div>That is some instance JSX code</div>

Anyway, codegen is beautiful candy. However you’ll be able to realize there is some hard-core
overloading going right here, so I assumed I would proportion how I typed this serve as
overloading with TypeScript.

One thing truly vital to remember is that the real codegen serve as
implementation is in truth a babel macro, so it appears not anything like the best way that
those purposes seem to paintings. It is referred to as all the way through the compilation procedure and
the arguments it is referred to as with is ASTs.

That mentioned, on the finish of the day, the patron’s revel in is all that issues,
so we’d like a model of the codegen serve as that works the best way it is anticipated.
So we will outline our varieties after which make certain that we forged the macro serve as
like an ordinary serve as.

import {createMacro} from 'babel-plugin-macros'
import sort {MacroHandler} from 'babel-plugin-macros'

const codegenMacro: MacroHandler = serve as codegenMacro(/* some args */) {
  // the implementation this is inappropriate
}

// use the `createMacro` application to show the codegenMacro right into a babel macro
const macro = createMacro(codegenMacro)

Good enough, so remember that the macro serve as is no longer in truth ever referred to as through
person code. This serve as can be referred to as through babel-plugin-macros, and it’s going to be
referred to as with the MacroHandler arguments. On the other hand, so far as TypeScript is
involved, the developer will be calling it, so we wish to give it the suitable
sort definitions and everybody can be satisfied. So let’s outline the ones:

// This handles the tagged template literal API:
claim serve as codegen(
  literals: TemplateStringsArray,
  ...interpolations: Array<unknown>
): any

// this handles the serve as name API:
claim serve as codegen(code: string): any

// this handles the `codegen.require` API:
claim namespace codegen {
  serve as require(modulePath: string, ...args: Array<unknown>): any
}

// Sadly I could not work out easy methods to upload TS strengthen for the JSX shape
// One thing in regards to the overload no longer being supported as a result of codegen cannot be the entire issues or no matter
// PRs welcome!

With the ones overloads outlined, now we simply wish to power TypeScript to regard our
macro document just like the codegen serve as now we have outlined. We additionally wish to make
this the default export of our macro document, so we will do all that directly:

export default macro as typeof codegen

You’ll peruse all of it in combination in
babel-plugin-codegen src/macro.ts document.

I am hoping that is helpful! Just right success to yah!

[ad_2]

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back To Top
0
Would love your thoughts, please comment.x
()
x