import type {Dependencies, Signature} from '../interfaces/type.js' import {$$typeToString, $$ts, $$define, $$raw, $$ident, $$escape} from 'ts-macros' import * as ts from 'typescript' export const square = (dep: Dependencies<'multiply', T>): Signature<'square', T> => z => dep.multiply(z, z) // z => dep.fooBar(z, z) // fails as desired // z => dep.multiply(z, 'foo') // still fails as desired // make sure ts-macros is running function $contains(value: T, ...possible: Array) { // repetition which goes over all the elements in the "possible" array return +["||", [possible], (item: T) => value === item]; } const searchItem = "needle"; console.log("In generic arithmetic") console.log($contains!(searchItem, "erwin", "tj")); // Transpiles to: console.log(false); // OK, record the type of square: square.reflectedType = //$$typeToString!(); $$typeToString!< (...args: DeepExpand>>) => DeepExpand>> >() type ShallowExpand = T extends unknown ? { [K in keyof T]: T[K] } : never; // Short alias of ShallowExpand for convenience below: type Deps = T extends unknown ? { [K in keyof T]: T[K] } : never; type ExpandedParameters any> = {[Index in keyof Parameters]: ShallowExpand[Index]>} type ExpandTwice = {[Index in keyof T] : ShallowExpand} type AsArray = T extends any[] ? T : []; type DeepExpand = T extends (...args: any) => any ? (...pars: AsArray>>) => DeepExpand> : T extends unknown ? { [K in keyof T]: DeepExpand } : never; type Out = ShallowExpand>; console.log("Deps type is", $$typeToString!<() => Out>()) type OutB = ExpandTwice>; console.log("Or perhaps", $$typeToString!()) console.log("Or maybe even", $$typeToString!< (...args: DeepExpand>>) => DeepExpand>> >()) // Now try to wrap it up in a macro // From the creator of ts-macros; temporary until next release function $export(name: string, value: unknown) { $$raw!((ctx, name: ts.Expression, value: ts.Expression) => { if (!ctx.ts.isStringLiteral(name)) throw ctx.error(name, "Expected a string literal."); return [ctx.factory.createVariableStatement([ctx.factory.createToken(ctx.ts.SyntaxKind.ExportKeyword)], ctx.factory.createVariableDeclarationList([ ctx.factory.createVariableDeclaration(name.text, undefined, undefined, value) ], ctx.ts.NodeFlags.Const))]; }); } // Obviously we'd prefer the name before the expression, but that won't work // until the next release function $exportImpl(expr: Impl, name: string) { $export!(name, expr); $$ident!(name).reflectedType = $$typeToString!< // (...args: DeepExpand>>) => DeepExpand>> // see comment in reflect below. Impl >(); } // works but then not visible at import with current ts-macros. // Author says he will be enhancing this "soon." $exportImpl!((dep: Dependencies<'multiply', T>): Signature<'square', T> => z => dep.multiply(z, z), 'squire') function $reflect(expr: Impl) { return $$escape!(() => { const temp: Impl & {reflectedType?: string} = expr; temp.reflectedType = $$typeToString!< // (...args: DeepExpand>>) => // DeepExpand>> // error; can't instantiate // Impl; even if we constrain it to be the type of a generic function, // that's still not a generic _type_, ugh. Impl>(); return temp; }) as Impl & {reflectedType: string} } export const squre = $reflect!( (dep: Dependencies<'multiply', T>): Signature<'square', T> => z => dep.multiply(z, z)) export const sqre = $reflect!( (dep: Deps>): Signature<'square', T> => z => dep.multiply(z, z))