feat: New array-style overloading
Step two didn't work so well, so this is actually step three. Avoids too much redundant type information.
This commit is contained in:
parent
c4bb415b5e
commit
8a2ae79c90
@ -4,8 +4,10 @@ Proof of concepts for a PocoMath-adjacent approach to a possible math.ts (TypeSc
|
||||
|
||||
Roadmap:
|
||||
|
||||
1. Install over.ts and get an example of add with number and bigint implementations working with it.
|
||||
2. Use the builder pattern to get add working with its implmentations defined incrementally before producing the final overload.
|
||||
1. Install over.ts and get an example of add with number and bigint implementations working with it. [DONE]
|
||||
2. Use the builder pattern to get add working with its implmentations defined incrementally before producing the final overload. [Didn't quite work the way we wnated, but maybe we can do an alternative later.]
|
||||
3. Make a version of over.ts, call it util/overload.ts, that takes an array of implementations without redundant type annotation. [DONE]
|
||||
4. Improve that version of overload with rtti to select the implementation without the implementations having to throw errors. [IN PROGRESS]
|
||||
3. Use the builder pattern to get a single object with both an add and a negate method, with both defined incrementally, working.
|
||||
4. Incorporate a subtract method that works on numbers and bigint by separate definitions but with dependencies on add and negate.
|
||||
5. Incorporate a subtract method that works with one generic implementation that works for both number and bigint with dependencies on add and negate.
|
||||
|
12
src/steps/three.ts
Normal file
12
src/steps/three.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import overload from '../util/overload.js'
|
||||
|
||||
const adder = overload([
|
||||
(x: number, y: number) => {
|
||||
if (typeof x === 'number' && typeof y === 'number') return x + y
|
||||
throw new TypeError('Can only add numbers')
|
||||
},
|
||||
(x: string, y: string) => 'Yay' + x + y
|
||||
])
|
||||
|
||||
console.log(adder(1, 2))
|
||||
console.log(adder('a', 'b'))
|
102
src/steps/two.ts
Normal file
102
src/steps/two.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { useTypes } from 'over.ts/src/index.js';
|
||||
|
||||
const types = {
|
||||
number: (x: unknown): x is number => typeof x === 'number',
|
||||
bigint: (x: unknown): x is bigint => typeof x === 'bigint'
|
||||
}
|
||||
|
||||
const overload = useTypes(types)
|
||||
|
||||
type TypeName = keyof typeof types
|
||||
|
||||
//type Tuple<T> = T | `${T}, ${Tuple<T>}`
|
||||
//type TypeTuple = Tuple<TypeName>
|
||||
|
||||
type CheckTuple<T> = T extends TypeName
|
||||
? T
|
||||
: T extends `${TypeName}, ${infer Rest}`
|
||||
? CheckTuple<Rest> extends string ? T : false
|
||||
: false
|
||||
|
||||
type CheckDecl<T> = T extends `${infer Args} -> ${TypeName}`
|
||||
? CheckTuple<Args> extends string ? T : never
|
||||
: never
|
||||
|
||||
function check<T extends string>(d: CheckDecl<T>) {
|
||||
console.log(d)
|
||||
}
|
||||
|
||||
check('number, bigint -> bigint')
|
||||
check('bigint -> number')
|
||||
|
||||
// check('string -> number') // would be error as desired
|
||||
|
||||
class ImpBuilder {
|
||||
addImp<T extends string, U>(decl: CheckDecl<T>, imp: U) {
|
||||
return Object.assign(this, {[decl]: imp} as Record<T, U>)
|
||||
}
|
||||
// finalize<T extends Record<string, unknown>>(this: T) {
|
||||
// return overload(this as Omit<T, 'addImp' | 'finalize'>)
|
||||
// }
|
||||
finalize() {
|
||||
delete this.addImp
|
||||
delete this.finalize
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
function fixImps<T extends {addImp: any}>(imps: T): Omit<T, 'addImp'> {
|
||||
const res = Object.assign({}, imps)
|
||||
delete res.addImp
|
||||
return res
|
||||
}
|
||||
|
||||
function overloadImps<T>(imps: T) {
|
||||
return overload(imps)
|
||||
}
|
||||
|
||||
const negateImps = new ImpBuilder()
|
||||
.addImp('number -> number', (a: number) => -a)
|
||||
.addImp('bigint -> bigint', (a: bigint) => -a)
|
||||
|
||||
const negate = overload(negateImps.finalize() as Omit<typeof negateImps, 'finalize'|'addImp'>)
|
||||
|
||||
console.log('Negation of 5 is', negate(5))
|
||||
console.log('Negation of 5n is', negate(5n))
|
||||
|
||||
const addImps = new ImpBuilder() // imagining this is in some "initialize a proto-bundle" file
|
||||
addImps.addImp('number, number -> number', (a: number, b: number) => a+b) // imagining this line in the `number` file
|
||||
const addAll = addImps.addImp('bigint, bigint -> bigint', (a: bigint, b: bigint) => a+b) // and this is in the `bigint` file
|
||||
const add = overload(addAll.finalize() as Omit<typeof addAll, 'finalize'|'addImp'>) // We wrap everything up elsewhere
|
||||
|
||||
console.log('Sum of 5 and 7 is', add(5,7))
|
||||
console.log('Sum of 5n and 7n is', add(5n, 7n))
|
||||
|
||||
try {
|
||||
//@ts-expect-error
|
||||
console.log('Mixed sum is', add(5n, 7))
|
||||
} catch {
|
||||
console.log('Mixed sum errored as expected.')
|
||||
}
|
||||
|
||||
/*****
|
||||
const addImps = new ImpBuilder()
|
||||
//addImps.addImp('number, number -> number', (a: number, b: number) => a+b)
|
||||
const subAddImps = addImps.addImp('number, number -> number', (a: number, b: number) => a+b).addImp('bigint, bigint -> bigint', (a: bigint, b: bigint) => {
|
||||
console.log('adding bigints')
|
||||
return a+b
|
||||
})
|
||||
//const finalAddImps = addImps.finalize()
|
||||
|
||||
const add = overload(subAddImps.finalize() as Omit<typeof subAddImps, 'finalize'|'addImp'>)
|
||||
|
||||
console.log('Sum of 5 and 7 is', add(5,7))
|
||||
console.log('Sum of 5n and 7n is', add(5n, 7n))
|
||||
|
||||
try {
|
||||
//@ts-expect-error
|
||||
console.log('Mixed sum is', add(5n, 7))
|
||||
} catch {
|
||||
console.log('Mixed sum errored as expected.')
|
||||
}
|
||||
****/
|
17
src/util/overload.ts
Normal file
17
src/util/overload.ts
Normal file
@ -0,0 +1,17 @@
|
||||
type UnionToIntersection<U> =
|
||||
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
|
||||
? I
|
||||
: never;
|
||||
|
||||
export default function overload<T extends readonly [...any[]]>(
|
||||
imps: T): UnionToIntersection<T[number]> {
|
||||
return <any>((...a: any[]) => {
|
||||
for (let i = 0; i < imps.length; ++i) {
|
||||
try {
|
||||
const val = imps[i](...a)
|
||||
return val
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
@ -5,7 +5,8 @@
|
||||
"rootDir": "src",
|
||||
"outDir": "obj"
|
||||
},
|
||||
"files": [
|
||||
"src/steps/one.ts"
|
||||
"include": [
|
||||
"src/steps/three.ts",
|
||||
"src/util/*.ts"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user