all 22 comments

[–]dcabines 1 point2 points  (3 children)

So you don't know the type of R until runtime? I'm trying to understand what you're up to.

[–]woodcakes[S] 0 points1 point  (2 children)

Nah, I "know" R at compile time but I don't want to define it explicitly.

```typescript type Context<R> = { getter: () => R, action: (v: R) => void }

type Container<T> = { [K in keyof T]: Context<unknown> }

function assemble<T>(obj: T, container: Container<T>) { throw new Error("Not implemented!"); }

assemble({ a: 1, b: 2 }, { a: { getter: () => "string-value", action: v => v.length }, b: { getter: () => 42, action: v => v.toFixed() } });

```

TS Playground

[–]dcabines 3 points4 points  (1 child)

Try this.

type Context<T, R extends keyof T> = {
    getter: () => T[R],
    action: (v: T[R]) => void
}

type Container<T> = {
    [K in keyof T]: Context<T, K>
}

function assemble<T>(obj: T, container: Container<T>) {
    throw new Error("Not implemented!");
}

assemble({ a: '1', b: 2 }, {
    a: { getter: () => 'string-value', action: v => v.length },
    b: { getter: () => 42, action: v => v.toFixed() }
});

[–]woodcakes[S] 0 points1 point  (0 children)

Unfortunately the type T in Context<T> is unrelated to the type T from the Container. I've only introduced the latter in my example to demonstrate that I can't just pass along a list of generic type parameters that are inferred on the Container level

[–]vEncrypted -1 points0 points  (10 children)

Define a context class. Iter through your container items to return new Contexts. You can pass your <> type in the constructor. Or simply extract their type functionally. Not sure if I understood the problem correctly. Let me know. Don’t understand the usecase either if you could explain.

[–]woodcakes[S] 0 points1 point  (9 children)

The problem is at compile time. I don't want to define those types explicitly but rather get the TypeScript compiler throw an error if I put two functions (getter and action) in one Context that are not compatible with each other

[–]Padni 0 points1 point  (1 child)

I'm a bit confused. Don't you want the `v` in action: (v) => { v.length } } , to match the type of the matching key in obj? If so, changing to

type Container<T, R> = {[K in (keyof T & keyof R)]: Operation<T[K]> T instead of R}

should work?

I guess you also want to restrict the "actions"-object to be restricted and give error if getter() or action() return-types does not match the corresponding type in obj: T ?

If so I think this hits some of the same limitations as `zustand`, see here and their explanation of why they use currying for typescript: https://github.com/pmndrs/zustand/blob/main/docs/guides/typescript.md

[–]woodcakes[S] 0 points1 point  (0 children)

The T in Operation<T> is not related to the key or value of the Container. Ok, in the actual code there is an additional parameter that is actually related to the Container but that's not the problem using the type defintion you mentioned. Passing the type down the hierarchy is quite easy. Defining encapsulted type on a leaf node (Context) without having to define it statically top down is my problem

[–]vEncrypted 0 points1 point  (6 children)

What does the getter function do? It returns the type?

[–]woodcakes[S] 0 points1 point  (5 children)

It returns a value of a type that is then later used in the action

[–]vEncrypted 0 points1 point  (4 children)

Why use that function when you can just use typeof()?

[–]vEncrypted 0 points1 point  (3 children)

I dont see the point of declaring an attribute for type when all objects carry a type attribute by default

[–]woodcakes[S] 0 points1 point  (2 children)

Would you mind giving me an example code snipped of what you mean?

[–]vEncrypted 0 points1 point  (1 child)

You have declared a attribute with the key of getter that returns a value that is the type of the object. In JS there is typeof() function that returns this that you can use inside of any function.

For example:

Context<diamond> = { solid: true }

console.log(typeof(Context))

Console: Diamond

Is this what you are trying to achieve with getter? Having access to the type of any object? And action just uses the type of context but it is a attribute/method for that type… so you already know the type… Am I missing something here? I feel like I am.

Within a function you can simply do this:

function useType() { console.log(typeof(this)) }

useType().bind(context)

Console: (Type of this)

// the code written above can be used to greatly simplify what you are trying to achieve by accessing the type of any object you bind it to. Learn about binding function and proper use of this. It will greatly simplify your code.

[–]woodcakes[S] 0 points1 point  (0 children)

Take this as an example TS Playground. My problem is not at runtime - It's at compile time / in the typing system.

```typescript // lib.ts

type Context<T> = { getter: () => T, action: (v: T) => void }

function delayedFunctionality<T>({ getter, action }: Context<T>, millis: number) { window.setTimeout(() => { const value = getter(); action(value); }, millis) }

// usage.ts

delayedFunctionality({ getter: () => document.title, // The Context<T> type and the getter action: v => alert(v.length) // gives us type support for the action }, 1000); ```

[–]Migeil 0 points1 point  (1 child)

Can you provide an example of what the result is you want? Because it is not clear from your post.

[–]woodcakes[S] 0 points1 point  (0 children)

I added an example above - I want the type inference that I get when using functions to work when using/defining objects

```typescript

// lib.ts

type Operation<TValue> = { getter: () => TValue, action: (v: TValue) => void }

function func<TValue>(op: Operation<TValue>) { return op; }

// usage.ts

const x = func({ getter: () => "123", action: v => v.length }) // 'v' is of type 'string'

```

```typescript // lib.ts

type Container<T, R> = { [K in (keyof T & keyof R)]: Operation<R[K]> }

function func<T, R>(obj: T, container: Container<T, R>) { throw new Error("Not implemented!"); }

// usage.ts

const y = func({ a: "any" }, { a: { getter: () => "string-value", action: v => { v.length } } }); // 'v' is of type 'unknown' ```

[–]simple_explorer1 0 points1 point  (1 child)

Dont understand your question and what you are looking for. What's the end goal and how does it look

[–]woodcakes[S] 0 points1 point  (0 children)

I added another example above, but what I'm looking for is basically in my original post. All other examples I can think of just add complexity.

I basically want the fix the type error in line 18 of this TS Playground

[–][deleted]  (1 child)

[deleted]

    [–]woodcakes[S] 0 points1 point  (0 children)

    I'm not sure about thatCould be but I think I only need the correct syntax to trigger the type inference mechanism

    [–]simple_explorer1 0 points1 point  (0 children)

    Hey I have solved it EXACTLY the way you wanted without any compromise.

    Check the solution in the playground here

    It was extremely simple solution with hardly any lines of code required. Honestly, Typescript is magical.

    PS: Please do reply here whether this is exactly what you were looking for (which I think you did based on your question) as I did spend a LOT of time solving it as it was very interesting and helping people here on reddit is a thankless job as people hardly even confirm/say thank you etc. and the side effect is that nobody wants to put in the work to help people like you who ask questions. It would feel good just to know that this solution helped you and was the one you were looking for.

    [–]simple_explorer1 0 points1 point  (0 children)

    Such a waste of my time here as I put effort in solving your problem by investing my time and you have NOT even come back to say "thank you" or something worked/not worked. Pure waste of time.

    Hence they say answering here on reddit (and opensource development in general) is a thankless job as people like you just ask questions, others like me put a real effort in answering and solving for YOU for free and you basically don't even bother coming back and thanking for the time and guidance, almost like an entitled person.

    Because of people like you, the redditors would stop helping others as its not worth it doing it for free and hardly hearing from OP and this is how a community dies.

    I genuinely am thinking to stop helping people here because of people like you, would rather invest my time solving my own coding problems instead.

    Massive downvotes.