I'm really close, and sure that an experienced typescript developer would solve this really quick.
I want to create a function that receives an object, and an optional function to extend this object.
- if no argument is given - the object is returned.
- if extend function is given(given arg is different from
undefined) - a new instance of this function with an updated initial object value is returned. - the object is also optional. you override the current value of the object if you pass this object.
usage for example:
let f1 = func(undefined, {'x': 10})
f1() // {x:10}
let f2 = func((pos: any) => ({...pos, y: 10}), {'x': 10}) //f2 => () => {x:number,y:number}
let f3 = f2() //f3 is {x:10,y:10}
f3 // {x:10,y:10}
let f4 = f2((pos: any) => ({...pos, y: 10}),{})
f4() // {y:10}
javascript implementation:
function func(extend?, obj?) {
if (extend) {
let newObj = extend(obj)
return ((extend, obj = newObj) => func(extend, obj))
} else return obj
}
so far javascript results work great. so I want to create a typescript function that will follow the exact type of this object.
solved thanks to @jcalz see real usecase
my own attempt
this is not a part of the question and it is only to show what I've already tried.
we will need to create a generic type function that returns a new generic if an arg given
type Primitive = bigint | boolean | null | number | string | symbol | undefined;
type PlainObject = Record<string, Primitive>;
type genericFuncType<K extends PlainObject, Tin extends K=K, Tout extends Tin=Tin, T extends ((pos: Tin) => Tout) = undefined> = T extends undefined ? K: genericFuncType<Tout, Tout, Tout>
let o = {x:10}
type ot = typeof o
to // {x: number}
type t1 = genericFuncType<ot>
t1 // {x: number}
type t2 = genericFuncType<ot,ot,ot,(pos:ot)=>ot>
t2 // a new function should be returned, but instead {x: number} is returned
well I couldn't make this generic work perfectly, but lets try it on the function, typescript func implementation:
function func<K extends PlainObject, Tin extends K, Tout extends Tin, T extends ((pos: Tin) => Tout) = undefined>(extend: T, obj: K = {} as any): genericFuncType<K, Tin, Tout, T> {
if (extend) {
let newObj = extend(obj as any)
return ((extend, obj = newObj) => func(extend, obj)) as any
} else return obj as any
}
let f1 = func(undefined, {'x': 10}) //f1 =>
typeof f1 // {x:number} - good
let f2 = func((pos: any) => ({...pos, y: 10}), {'x': 10}) //f2 => () => {x:number,y:number}
typeof f2 // {x:number} - bad
what I'm doing wrong? why I can't return a new function with extended type recursively?
(for reference only - here's a playground for very similar function that works 2 levels deep - but nut recursively deep)
anyanyis just uselessanyis the right choice. It's much like using the base object in a strongly typed language, i.e.objectin C#f1 is not a function. If I ignore that and try to give typings to thefunc()function JavaScript implementation, I get this. If that meets your needs, I'll write up an answer. Note that you can't pass in anextendlike(pos: any) => ({...pos, y: 10}), which is of type(pos: any) => any... you'll just getanyout; if you want the compiler to do something better, you need a generic like<T>(pos: T) => ....PrimitiveandPlainObjectmeaningfully contribute to the behavior here? The fact that you're dealing with objects and not primitives seems like you could just remove that for the sake of the question; given your implementation. For example,func((x: number) => x + 1, 100)should be fine), so that people who want to help can focus on the issue.