Core concepts

Writing an imaginary function

Learn how to write an imaginary function that will add bits of human-like intelligence to your code.


Once you have an idea for what you want your imaginary function to do, it's time to write it. Imaginary functions take the form of TypeScript function declaration. They look like this:

/**
 * This function takes in a word or phrase and returns a single emoji that represents that concept.
 * @param textToEmojify - textToEmojify the word or phrase to be turned into an emoji
 * @returns a single character emoji
 * @imaginary
 */
export declare function singleEmojiForText(
  textToEmojify: string
): Promise<string>;

Every imaginary function starts with a TSDoc comment that describes what the function does. This is where you express clearly what you want the function to do. Try to be more clear and prescriptive in the function comment than you normally would be in code comments; remember that this is the main passage of text that the AI will use to determine what you are trying to achieve.

It's a good best practice to use the standard TSDoc @param and @returns directives to describe in natural language the parameters and expected return value for the function. You must also include the @imaginary directive to tell the imaginary-dev compiler that this is a function that will use AI for its implementation.

Comments must be TSDoc form and must have @imaginary

It is critical that the comment be a TSDoc style comment; it must start with /** (not /* or //). It is also critical that you include @imaginary in the comment. If you don't do either these things, the function will not be transformed by the imaginary-dev compiler.

After the function comment, you will need a TypeScript function declaration. That consists of the keywords declare function, followed by a function name, a parameter list in parentheses, and a return value. Both the function name and the parameter names should be as descriptive as possible. All parameters must have TypeScript type annotations to give imaginary-dev a better idea of what the function is doing.

The return value for the imaginary function must be a Promise of another type, as imaginary functions are always asynchronous. If you forget to declare your imaginary function as returning a Promise, you will get a compile-time error from imaginary-dev suggesting that you return a Promise instead of a synchronous type.

Types in imaginary functions

All types in an imaginary function, both for parameters and for the promised return value, must be JSON-serializable and must not use type aliases. Classes or interfaces are also not allowed.

type RecipeIngredient = { name: string; quantity?: number; units?: string }
/**
 * This function takes in a recipe and returns an array of the recipe ingredients.
 * @param recipe - a string that contains the recipe, which will have an ingredient list,
 * instructions, and maybe a story.
 * @returns - an array of all the ingredients in the recipe, parsed out with a name and an
 * optional quantity and units.
 * @imaginary
 */
// WRONG cannot use type alias; will not compile.
declare function parseRecipe(recipe: string): Promise<RecipeIngredient[]>

// CORRECT uses inline type annotations.
declare function parseRecipe(
  recipe: string
): Promise<{ name: string; quantity?: number; units?: string }[]>

Define your types inline in the imaginary function prototype. Be a little wary of defining your types with too many levels of complexity. The AI behind imaginary-dev is surprisingly good at parsing complex types, but it may not be able to fully understand an object model with a dozen levels of hierarchy.

Note that GPT can be a little fickle about returning syntactically correct JSON, so the imaginary-dev runtime engine will leniently parse GPT's response and validate the return value at runtime against the TypeScript types you put in your imaginary function definition. So, unlike other TypeScript functions in your code, you have a runtime guarantee that the return type from an imaginary function will be correct.

Once you think you have your imaginary function working properly, build it with the imaginary-dev compiler, and see how it performs with different test arguments. If it isn't giving you what you think you want, try tweaking the comment, the function name, or the parameter names to be more precise and specific to what the function should or should not do. In this process, it's normal to rapidly iterate and evolve your imaginary function definition as you get a feel for how the AI responds.

Have problems or questions? Check out the Troubleshooting Guide.

Previous
Use cases