I put together a simple Store implementation which wraps an immutable javascript 'root' item as state.

A partition operation allows a child Store to be derived from a parent store using a key. The new Store's root state is the child of the original root state as found at that key. The child store otherwise functions the same as the root store.

The implementation seems to work fine, but unfortunately I have a compilation error for the partition operation when (for other reasons) I'm required to limit possible root items to extends object. Of course only some child items will satisfy this constraint, meaning typescript spots a possible error from recursive partitioning.

I think I need to limit partition key arguments to keys which resolve to a child that extends object. I am unsure how to do so.

Everything is working well in terms of implementation and typings until this intervention.

A definition which looked positive and compiled (before adding the object constraint) is this one...

type Partitioner<State> = <Key extends keyof State>(key:Key) => Store<State[Key]>;

interface Store<State> {
    read():State
    write(state:State):void;
    partition: Partitioner<State>
}

class BasicStore<State> implements Store<State> {
    state:State;
    constructor(state:State){
        this.state = state;
    }
    read = () => this.state;
    write = (state:State) => this.state = state;
    partition:Partitioner<State> = <Key extends keyof State>(key:Key) => new BasicPartition(this, key);
}

export class BasicPartition<State, Key extends keyof State>
  implements Store<State[Key]> {
  constructor(readonly store: Store<State>, readonly key: Key) {}
  read = () => this.store.read()[this.key];
  write = (state:State[Key]) => this.store.write({
      ...this.store.read(),
      [this.key]:state
  })
  partition:Partitioner<State[Key]> = <SubKey extends keyof State[Key]>(subKey:SubKey) => new BasicPartition(this, subKey);
}

See typescript playground

The compilation error can be seen in the below version of the same code where BasicStore and BasicPartition limit their State to extends object...

type Partitioner<State> = <Key extends keyof State>(key:Key) => Store<State[Key]>;

interface Store<State> {
    read():State
    write(state:State):void;
    partition: Partitioner<State>
}

class BasicStore<State extends object> implements Store<State> {
    state:State;
    constructor(state:State){
        this.state = state;
    }
    read = () => this.state;
    write = (state:State) => this.state = state;
    partition:Partitioner<State> = <Key extends keyof State>(key:Key) => new BasicPartition(this, key);
}

export class BasicPartition<State extends object, Key extends keyof State>
  implements Store<State[Key]> {
  constructor(readonly store: Store<State>, readonly key: Key) {}
  read = () => this.store.read()[this.key];
  write = (state:State[Key]) => this.store.write({
      ...this.store.read(),
      [this.key]:state
  })
  partition:Partitioner<State[Key]> = <SubKey extends keyof State[Key]>(subKey:SubKey) => new BasicPartition(this, subKey);
}

An error is reported from the last line Argument of type 'this' is not assignable to parameter of type 'Store<object>' See typescript playground

How can I limit valid Key arguments to the partition function so that State[Key] is always an object and therefore eliminate the invalid code path. In pseudo-code I think the Partitioner generic argument might look something like Key extends keyof State where State[Key] extends object. Where this union resolved to never there would be no valid arguments to call partition at all.

0

There are 0 best solutions below