feat: Demonstrate macros to encapsulate the type-reflection process
This commit is contained in:
parent
af02f1cb29
commit
9eff2bc265
@ -1,5 +1,6 @@
|
|||||||
import type {Dependencies, Signature} from '../interfaces/type.js'
|
import type {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
import {$$typeToString} from 'ts-macros'
|
import {$$typeToString, $$ts, $$define, $$raw, $$ident, $$escape} from 'ts-macros'
|
||||||
|
import * as ts from 'typescript'
|
||||||
|
|
||||||
export const square =
|
export const square =
|
||||||
<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
||||||
@ -19,15 +20,23 @@ console.log($contains!(searchItem, "erwin", "tj"));
|
|||||||
// Transpiles to: console.log(false);
|
// Transpiles to: console.log(false);
|
||||||
|
|
||||||
// OK, record the type of square:
|
// OK, record the type of square:
|
||||||
square.reflectedType = $$typeToString!<typeof square>();
|
square.reflectedType = //$$typeToString!<typeof square>();
|
||||||
|
$$typeToString!<
|
||||||
|
<T>(...args: DeepExpand<Parameters<typeof square<T>>>)
|
||||||
|
=> DeepExpand<ReturnType<typeof square<T>>>
|
||||||
|
>()
|
||||||
|
|
||||||
type ShallowExpand<T> = T extends unknown ? { [K in keyof T]: T[K] } : never;
|
type ShallowExpand<T> = T extends unknown ? { [K in keyof T]: T[K] } : never;
|
||||||
|
// Short alias of ShallowExpand for convenience below:
|
||||||
|
type Deps<T> = T extends unknown ? { [K in keyof T]: T[K] } : never;
|
||||||
type ExpandedParameters<T extends (...args: any) => any> = {[Index in keyof Parameters<T>]: ShallowExpand<Parameters<T>[Index]>}
|
type ExpandedParameters<T extends (...args: any) => any> = {[Index in keyof Parameters<T>]: ShallowExpand<Parameters<T>[Index]>}
|
||||||
type ExpandTwice<T extends any[]> = {[Index in keyof T] : ShallowExpand<T[Index]>}
|
type ExpandTwice<T extends any[]> = {[Index in keyof T] : ShallowExpand<T[Index]>}
|
||||||
type AsArray<T> = T extends any[] ? T : [];
|
type AsArray<T> = T extends any[] ? T : [];
|
||||||
type DeepExpand<T> =
|
type DeepExpand<T> =
|
||||||
T extends (...args: any) => any ? (...pars: AsArray<DeepExpand<Parameters<T>>>) => DeepExpand<ReturnType<T>> :
|
T extends (...args: any) => any
|
||||||
|
? (...pars: AsArray<DeepExpand<Parameters<T>>>) => DeepExpand<ReturnType<T>> :
|
||||||
T extends unknown ? { [K in keyof T]: DeepExpand<T[K]> } : never;
|
T extends unknown ? { [K in keyof T]: DeepExpand<T[K]> } : never;
|
||||||
|
|
||||||
type Out<T> = ShallowExpand<Dependencies<'multiply', T>>;
|
type Out<T> = ShallowExpand<Dependencies<'multiply', T>>;
|
||||||
|
|
||||||
console.log("Deps type is", $$typeToString!<<T>() => Out<T>>())
|
console.log("Deps type is", $$typeToString!<<T>() => Out<T>>())
|
||||||
@ -39,3 +48,52 @@ console.log("Or perhaps", $$typeToString!<OutB>())
|
|||||||
console.log("Or maybe even", $$typeToString!<
|
console.log("Or maybe even", $$typeToString!<
|
||||||
<T>(...args: DeepExpand<Parameters<typeof square<T>>>) => DeepExpand<ReturnType<typeof square<T>>>
|
<T>(...args: DeepExpand<Parameters<typeof square<T>>>) => DeepExpand<ReturnType<typeof square<T>>>
|
||||||
>())
|
>())
|
||||||
|
|
||||||
|
// 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<Impl>(expr: Impl, name: string) {
|
||||||
|
$export!(name, expr);
|
||||||
|
$$ident!(name).reflectedType = $$typeToString!<
|
||||||
|
// <T>(...args: DeepExpand<Parameters<Impl<T>>>) => DeepExpand<ReturnType<Impl<T>>> // 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!(<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
||||||
|
z => dep.multiply(z, z),
|
||||||
|
'squire')
|
||||||
|
|
||||||
|
function $reflect<Impl>(expr: Impl) {
|
||||||
|
return $$escape!(() => {
|
||||||
|
const temp: Impl & {reflectedType?: string} = expr;
|
||||||
|
temp.reflectedType = $$typeToString!<
|
||||||
|
// <T>(...args: DeepExpand<Parameters<Impl<T>>>) =>
|
||||||
|
// DeepExpand<ReturnType<Impl<T>>> // 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!(
|
||||||
|
<T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
||||||
|
z => dep.multiply(z, z))
|
||||||
|
|
||||||
|
export const sqre = $reflect!(
|
||||||
|
<T>(dep: Deps<Dependencies<'multiply', T>>): Signature<'square', T> =>
|
||||||
|
z => dep.multiply(z, z))
|
||||||
|
14
src/index.ts
14
src/index.ts
@ -21,3 +21,17 @@ console.log('Result is', myabs)
|
|||||||
|
|
||||||
// Check type of the generic square implementation
|
// Check type of the generic square implementation
|
||||||
console.log('Type of square is', Specifications.generic.square.reflectedType)
|
console.log('Type of square is', Specifications.generic.square.reflectedType)
|
||||||
|
|
||||||
|
// Now check the ones that came via macros:
|
||||||
|
|
||||||
|
// Auto-generated export is invisible to TypeScript at the moment, author
|
||||||
|
// says he will fix:
|
||||||
|
console.log('Type of squire (auto-exported) is',
|
||||||
|
// @ts-ignore
|
||||||
|
Specifications.generic.squire.reflectedType)
|
||||||
|
|
||||||
|
// Via a macro wrapper around the definition, two ways:
|
||||||
|
console.log('Type of squre (unexpanded) is',
|
||||||
|
Specifications.generic.squre.reflectedType)
|
||||||
|
console.log('Type of sqre (expanded) is',
|
||||||
|
Specifications.generic.sqre.reflectedType)
|
||||||
|
Loading…
Reference in New Issue
Block a user