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:
|
Roadmap:
|
||||||
|
|
||||||
1. Install over.ts and get an example of add with number and bigint implementations working with it.
|
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.
|
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.
|
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.
|
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.
|
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",
|
"rootDir": "src",
|
||||||
"outDir": "obj"
|
"outDir": "obj"
|
||||||
},
|
},
|
||||||
"files": [
|
"include": [
|
||||||
"src/steps/one.ts"
|
"src/steps/three.ts",
|
||||||
|
"src/util/*.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user