33 lines
1.4 KiB
TypeScript
33 lines
1.4 KiB
TypeScript
import type {Dependencies, Signature} from '../interfaces/type.js'
|
|
|
|
// This stuff would really go in interfaces/type.js or someplace like that
|
|
type Annotate<U> = U & {dependencies?: Record<string, string>, signature?: string}
|
|
|
|
function genericImplementation<Deps extends Record<string, string>, Sig extends string>(
|
|
deps: Deps, sig: Sig, impl: <T>(dep: Dependencies<keyof Deps, T>) => Signature<Sig, T>
|
|
) {
|
|
const rimpl: Annotate<typeof impl> = impl
|
|
rimpl.dependencies = deps
|
|
rimpl.signature = sig
|
|
return rimpl
|
|
}
|
|
// and of course it would need to be generalized somewhat from the above
|
|
// --------------
|
|
|
|
// Then here for the implementation we would have:
|
|
export const square = genericImplementation(
|
|
{multiply: 'T'}, 'square', <T>(dep) => z => dep.multiply(z, z)
|
|
)
|
|
|
|
// Note this was previously
|
|
|
|
// export const square =
|
|
// <T>(dep: Dependencies<'multiply', T>): Signature<'square', T> =>
|
|
// z => dep.multiply(z, z)
|
|
|
|
// but then we couldn't get at the dependencies or the signature at runtime. So the point of
|
|
// genericImplementation is that it takes literal values for the dependencies and signature,
|
|
// from which it deduces the types of the actual dependencies of the implementation and the
|
|
// the implementation itself, and then it copies those literal values onto properties of the
|
|
// implementation, where they can be accessed at runtime as illustreated in ../index.ts
|