Get Off My Lawn
Get Off My Lawn

Reputation: 36311

How to define properties with a Proxy

I have a class with a Proxy in it which looks like this:

export class Row<T extends ModelItems> {

  private _row: T = <T>{}

  public constructor(rowItems?: T) {
    if (rowItems) { this._row = rowItems }
    return new Proxy(this, {
      get: function (target, prop) {
        return (<any>target)[prop] || target._row[prop] || ''
      }
      /* set(): snipped */
  }
}

When I do this:

interface User {
    id: number
    first: string
    last: string
    username: string
}

let row = new Row<User>({/* My object */})

How can I get it so that when I type row I get a list of items that are defined in the User interface. Right now all that gets suggested are the actual methods/properties that are in the root of the object.

I have tried this:

export interface Row<T extends ModelItems> {
  [key: string]: T 
}

This however doesn't seem to help at all other than telling me that my methods aren't defined propertly

Upvotes: 0

Views: 111

Answers (1)

jcalz
jcalz

Reputation: 328342

It looks like you want an instance of Row<T> to also be a T itself; is that right? TypeScript won't let you do something like class Row<T> implements T, nor will it easily infer that itself. Instead you could do something like the following:

export class _Row<T> {
 // ... your definition above
}
// Row type alias
export type Row<T extends ModelItems> = _Row<T> & T;
// Row value alias
export const Row = _Row as {
  new <T>(rowItems?: T): Row<T>
  prototype: _Row<{}>
}

This behaves more or less the same at runtime, but now TypeScript will understand that an instance of Row<T> has all the properties that T has:

declare const u: User;
let row = new Row(u); // inferred as Row<User>
row.username // string

If you are trying to represent something different, like a Row<T> behaves like something with all the keys of T but with possibly-empty-string properties, you could replace the // Row type alias with this instead:

// Row type alias
export type Row<T extends ModelItems> = _Row<T> &
  {[K in keyof T]: T[K] | ''};

And verify that it behaves as you expect:

declare const u: User;
let row = new Row(u); // inferred as Row<User>
row.id // number || ''

Hope that helps; good luck!

Upvotes: 2

Related Questions