10

I would like to try some things around Proxies, but i struggle to get the simplest form running.

I have the following code

const myObj = {
  x: 'Hello',
};

const p = new Proxy(myObj, {
  get: (target, key) => {
    return key in target ? target[key] + ' World!' : 'nope';
  },
});

console.log(p.x);

And i get the following error, but i have no clue why and how to solve it:

index.ts:7:28 - error TS7053: Element implicitly has an 'any' type because expression of type 'string | number | symbol' can't be used to index type '{ x: string; }'.
  No index signature with a parameter of type 'string' was found on type '{ x: string; }'.

7     return key in target ? target[key] + ' World!' : 'nope';
                             ~~~~~~~~~~~

Found 1 error.

I think TS should be able to infer everything. What do i miss here?

VSDekar
  • 1,741
  • 2
  • 21
  • 36
  • TS *can* infer everything, you've asked it not to by setting noImplicitAny in your compiler options, so when you declare the parameter `target` you need to give it a type. – Antony Dec 18 '19 at 11:01

5 Answers5

9

This is because the key is defined as PropertyKey. Meaning it could be string | number | symbol which is different index signature than your object which only has one key x. It's not really error in types - TS cannot be statically sure that your object won't be called with something other than x and assumes broader range on possible key values. One solution to this could be assuring TypeScript that it indeed will be a keyof myObj, since you are already doing a runtime check yourself:

return key in target ? target[key as keyof typeof target] + ' World!' : 'nope';
rafal2228
  • 396
  • 1
  • 5
  • You solutions works, but shouldn't be TS already sure with the `key in target` check that i only use a valid key? – VSDekar Dec 18 '19 at 12:37
  • This might be related to https://github.com/microsoft/TypeScript/issues/21732 , but unfortunately TypeScript cannot do that at this moment. – rafal2228 Dec 18 '19 at 14:50
5

Thx to @Rafal2228 for explaining the problem and suggesting a workaround. Just for completeness i want to post how i have fixed it now. (With a TypeGuard)

const hasKey = <T extends object>(obj: T, k: keyof any): k is keyof T =>
  k in obj;

const myObj = {
  x: 'Hello',
};

const p = new Proxy(myObj, {
  get: (target, key) => {
    return hasKey(target, key) ? target[key] + ' World!' : 'nope';
  },
});

console.log(p.x);
VSDekar
  • 1,741
  • 2
  • 21
  • 36
3

I'm currently using

const someProxy = new Proxy(someObj, {
  get(target: typeof someObj, prop: keyof typeof someObj) {
    return target[prop];
  },
});

This will activate typecheck during writing the code.

But I'm tired of typing it again and again. Looking for a better solution now.

Pablo LION
  • 1,294
  • 6
  • 20
0

You can explicitly specify the type of the key parameter:

const myObj = {
  x: 'Hello',
};

const p = new Proxy(myObj, {
  get: (target, key: keyof typeof myObj) => {
    return key in target ? target[key] + ' World!' : 'nope';
  },
});

console.log(p.x);
Luis
  • 1
  • 1
-2

What I discovered recently is that

when you declare a local variable using the const keyword and initialize it with a literal value, TypeScript will infer a literal type for that variable (from https://mariusschulz.com/blog/literal-type-widening-in-typescript)

const numericLiteral = 42;     // Type 42

See https://www.typescriptlang.org/docs/handbook/variable-declarations.html, https://mariusschulz.com/blog/literal-type-widening-in-typescript, https://blog.logrocket.com/const-assertions-are-the-killer-new-typescript-feature-b73451f35802/.

Also this may help you - https://medium.com/@SylvainPV/type-safety-in-javascript-using-es6-proxies-eee8fbbbd600.

You are not declaring literal constant, but something similar may be happening too. And I suggest to declare an interface to "support" the indexing.

Karel Frajták
  • 4,389
  • 1
  • 23
  • 34