2022-12-24 19:17:36 +00:00
|
|
|
/**
|
|
|
|
* Idea: instead of writing TypeScript, and inferring the JS-pocomath signature
|
|
|
|
* from TS that via a TypeScript plugin, we can maybe do this the other way
|
|
|
|
* around: take the JS-pocomath signature as base (and source of truth), and
|
|
|
|
* infer TypeScript interfaces from that using infer in template literal types.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const create = createFactory<{
|
2023-09-14 15:42:42 +00:00
|
|
|
number: number
|
|
|
|
bigint: bigint
|
|
|
|
string: string
|
|
|
|
// T: T // TODO: how to do generics?
|
|
|
|
// 'Complex<T>': unknown // TODO: how to do generics?
|
2022-12-24 19:17:36 +00:00
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: how to pass config?
|
2023-09-14 15:42:42 +00:00
|
|
|
// These are the interfaces:
|
|
|
|
const Multiply = 'multiply(number,number)=>number'
|
|
|
|
const Zero = 'zero(number)=>number'
|
2022-12-24 19:17:36 +00:00
|
|
|
|
2023-09-14 15:42:42 +00:00
|
|
|
const createSquare = create([Multiply, Zero],dep => {
|
|
|
|
return (x: number) => dep.multiply(x, x)
|
|
|
|
})
|
2022-12-24 19:17:36 +00:00
|
|
|
|
|
|
|
// the code works in JS, and works in TS
|
|
|
|
const multiply = (a: number, b: number) => a * b
|
2023-09-14 15:42:42 +00:00
|
|
|
const zero = (a: number) => 0
|
|
|
|
const square = createSquare({ multiply, zero })
|
2022-12-24 19:17:36 +00:00
|
|
|
console.log('square', square(8)) // 64
|
|
|
|
|
|
|
|
function createFactory<BaseTypes extends Record<string, unknown>>() {
|
2023-09-14 15:42:42 +00:00
|
|
|
type BaseTypeNames = string & keyof BaseTypes
|
|
|
|
type ResolveType<TypeName extends BaseTypeNames> = BaseTypes[TypeName]
|
|
|
|
|
|
|
|
type Value<K> = K
|
|
|
|
|
|
|
|
type ResolveArguments<S extends string> = S extends ''
|
|
|
|
? []
|
|
|
|
: S extends `${infer Arg extends BaseTypeNames},${infer Tail}`
|
|
|
|
? [ResolveType<Arg>, ...ResolveArguments<Tail>]
|
|
|
|
: S extends `${infer Arg extends BaseTypeNames}`
|
|
|
|
? [ResolveType<Arg>]
|
|
|
|
: never
|
|
|
|
|
|
|
|
type FunctionDefinition<FnType> =
|
|
|
|
FnType extends Value<infer K>
|
|
|
|
? K extends `${infer Name}(${infer Args})=>${infer ReturnType extends BaseTypeNames}`
|
|
|
|
? Record<Name, (...args: ResolveArguments<Args>) => ResolveType<ReturnType>>
|
|
|
|
: never
|
|
|
|
: never
|
|
|
|
|
|
|
|
// inspired by: https://stackoverflow.com/questions/68391632/infer-type-from-array-literal
|
|
|
|
type DepRecord<
|
|
|
|
Arr extends Array<Value<any>>,
|
|
|
|
Result extends Record<string, any> = {}
|
|
|
|
> = Arr extends []
|
|
|
|
? Result
|
|
|
|
: Arr extends [infer H, ...infer Tail]
|
|
|
|
? Tail extends Array<Value<any>>
|
|
|
|
? H extends Value<any>
|
|
|
|
? DepRecord<Tail, Result & FunctionDefinition<H>>
|
|
|
|
: never
|
|
|
|
: never
|
|
|
|
: never
|
|
|
|
|
|
|
|
return function create<K extends string, Dependencies extends Value<K>[], W>(
|
|
|
|
dependencies: [...Dependencies],
|
|
|
|
callback: (deps: DepRecord<[...Dependencies]>) => W
|
|
|
|
) {
|
|
|
|
console.log('Creating typed-function with dependencies:', dependencies)
|
|
|
|
// TODO: create a typed-function for real
|
|
|
|
return callback
|
|
|
|
}
|
2022-12-24 19:17:36 +00:00
|
|
|
}
|