41

I have an interface type called IRawParams which simply specifies a string key and any vals.

interface IRawParams {
    [key: string]: any
}

I have a class, ParamValues which contains some behavior on top of keys/values. I'd like to specify that the ParamValues class implements IRawParams interface.

class ParamValues implements IRawParams {
    parseFromUrl(urlString: string) {
        Parser.parse(urlString).forEach(item => this[item.key] = item.val);
    }
}

// so I can do something like this
var params = new ParamValues();
params.parseFromUrl(url);
var userId = params.userId;

When I attempt this, I get a compiler error:

error TS2420: Class 'ParamValues' incorrectly implements interface 'IRawParams'. Index signature is missing in type 'ParamValues'.

Can I get my class to implement the IRawParams Interface, or otherwise get Typescript to allow instances of my class to be compatible with an indexed type {[key: string]: any}?

Chris T
  • 8,186
  • 2
  • 29
  • 39

1 Answers1

57

To define a class with an index signature, just write the index signature in the class:

interface IRawParams {
    [key: string]: any
}

class Foo implements IRawParams {
    [k: string]: any;
}
Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • 2
    Thanks! Now I feel like an idiot. I was wrapping the signature with curly braces: `{[key: string]: any}` – Chris T Aug 13 '15 at 13:46
  • 4
    And how to implement it? i.e. how to write the code that pams the index to a value? – Jose Dec 25 '18 at 12:52
  • 2
    @Jose In the class of `Foo` you can access values like so: `getme(key: string) { return this[key]}`. You can pass values into the constructor of `Foo` with `let foo = new Foo({ some: "raw", [0]: "params" })`. The constructor of `Foo` could receive the values like so: `constructor(values: IRawParams = { } as IRawParams) { Object.keys(values).map(key => { this[key] = values[key] }) }` and you could get the values from the `Foo` object with `foo.getme("some")` or directly `foo["some"]`. – Martin Braun Jan 16 '19 at 17:59
  • 2
    also note `[key: string]: any` in the interface will include numbers as indexes, too. If you only want to allow strings as keys of your object, just add `[index: number]: undefined` to the interface. – Martin Braun Jan 16 '19 at 18:00
  • 4
    So the values must be pre-set. Is there anyway to link a function to determine the value of a key? i.e. if someone uses my class from outside, Foo['tralara'] I would like to be hable to handle the key value in a function in my class and be able to determine dynamically the value to return. – Jose Jan 20 '19 at 06:19
  • I think what Jose is asking for is akin to Ruby's `method_missing` -- a getter that's called for arbitrary keys on the object. You can look at [Proxy objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), but those only work on newish runtimes. – Coderer Sep 21 '20 at 15:22