MBash
MBash

Reputation: 11

Dynamically setting expected type for function in class

Suppose I have the following interface:

interface Foo {
  x: {
    x1: string;
    x2: {
       x3: number; 
    }
  };
}

I want to write a class where a function automatically figures out the valid arguments based on the type.

For example:

const x = someNewClass();
x
 .ref('x')
 .ref('x1 | x2') // we pass either x1 or x2
 .ref('x3').getAsPath(); // if we pass x2, we see x3, otherwise undefined

The values I've written should auto-complete, and the function should return the path: x/x2/x3';

I don't know typescript very well -- I don't even know if this is possible! However, I got this far (which admittedly isn't very far):

class SomeHelperClass<T> {
  public ref(d: keyof T) {
    // can we return a new instance of a this class initialized with
    // T taken as a partial of the original type starting from `d`?
    return this;
  }
  public getAsPath() {
    // ...
  }
}

class Test extends SomeHelperClass<Foo> {}

const t = new Test();

t.ref('x').ref('...');

The first ref call of course works, but I am completely lost after that.

Upvotes: 0

Views: 53

Answers (1)

MBash
MBash

Reputation: 11

Thanks to @SergiuParaschiv's link, I found a working solution for my exact issue. I am sharing the code here in case the github repos or the references disappear for any reason.

interface PathResult<T> {
    <TSubKey extends keyof T>(key: TSubKey): PathResult<T[TSubKey]>;
    /**Gets the resulting path */
    path: string[];
}

/**
 * Create a deep path builder for a given type
 */
export function path<T>() {
    /**Returns a function that gets the next path builder */
    function subpath<T, TKey extends keyof T>(parent: string[], key: TKey): PathResult<T[TKey]> {
        const newPath = [...parent, key];
        const x = (<TSubKey extends keyof T[TKey]>(subkey: TSubKey) => subpath<T[TKey], TSubKey>(newPath, subkey)) as PathResult<T[TKey]>;
        x.path = newPath;
        return x;
    }

    return <TKey extends keyof T>(key: TKey) => subpath<T, TKey>([], key);
}

To see the solution in action, please visit the author's repository.

Thanks again Sergiu!

Upvotes: 1

Related Questions