Code
let obj = {
prop1: function () { },
prop2: 'hello',
}
let prox = new Proxy(obj, {
get: function (target, name) {
return 5;
}
});
prox.prop1.
Expected behavior:
I would expect that when I type prox.prop1., I would get typescript suggestions for Number.prototype, but instead, I get suggestions for Function.prototype. prox.prop1 will (according to typescript) still be callable as a function, but in runtime, it will clearly be a number and will throw an exception.
Statically evaluate the proxy traps and determine the type of thing being returned to offer proper typescript intellisense.
Here's a screenshot of the issue.

You're right, our current definition of Proxy assumes that the output type is the same as the input type, when it could be any type, and just happen to want access to target.
Maybe we could do something like this:
interface ProxeeHandler<T extends object, TOut extends object> {
get?<K extends keyof TOut>(target: T, p: K, receiver: TOut): TOut[K];
set?<K extends keyof TOut>(target: T, p: K, value: TOut[K], receiver: TOut): boolean;
}
interface ProxeeConstructor {
new <T extends object, TOut extends object>(target: T, handler: ProxeeHandler<T, TOut>): TOut;
}
declare var Proxee: ProxeeConstructor;
let obj = {
prop1: function () { },
prop2: 'hello',
}
// Type inference can't infer what keys the result is supposed to have.
let prox: Record<keyof typeof obj, number> = new Proxee(obj, {
get(target, name) {
return 5;
}
});
Out of my head I can tell about 3 cases of Proxy changing interface of a target object:
Array methodsArray methods immutable.So the idea of In and Out types seems reasonable as it covers all of these cases. But to have soundly typed traps it still would require improved return type checking in switch and if statements as this is a valid code now:
interface FooBar {
foo: number;
bar: string;
}
function fooOrBar<K extends keyof FooBar>(prop: K): FooBar[K] {
switch (prop) {
case "foo": return "foo"; //It should cause a type error
case "bar": return 5; // And this too
}
}
I would love proxies to be able to solve what @andy-ms is saying because my use case is where input/output types are different.
I'm desperate because I've wanted Scalas underscore in typescript for a long time, I have the implementation but there's no way to type it here's an example.
I need a way to tell typescript that _.child is actually the converted output type...
and that _.child.children is...
const buildUnderscore = (f: any) => (path: any[]): any => new Proxy(f, handler(path));
const handler = (path: any[]) => ({
get: function(obj: any, prop: any, value: any) {
return buildUnderscore((a: any) => getAtPath(a, path.concat(prop)))(path.concat(prop));
}
})
/* takes object A and key path ["a", "b", "c"] -> returns A["a"]["b"]["c"] */
const getAtPath = (obj: any, [head, ...tail]: string[]): any => {
if(head === undefined) return obj;
return getAtPath(obj[head], tail);
}
/* can be used to short-hand most lambdas now */
const _: any = buildUnderscore((a: any) => a)([]);
const data = [
{
child: {
name: "bob",
children: ["sue", "john"]
}
},
{
child: {
name: "catness",
children: ["rue", "hew"]
}
},
]
console.log(data.map(_)) // converts to data.map((a) => a)
console.log(data.map(_.child.children[0]))// converts to: data.map((a) => a["child"]["children"][0])
console.log(data.map(_.child)) // converts to data.map((a) => a["child"])
I stumble upon this. Might be too late... but you could try
interface MyProxyConstructor {
new <T, H extends object>(target: T, handler: ProxyHandler<H>): H
}
const MyProxy = Proxy as MyProxyConstructor
let obj = {
prop1: function() {},
prop2: 'hello',
}
let prox = new MyProxy<typeof obj, { [name: string]: () => number }>(obj, {
get: function() {
return 5
},
})
prox.prop1 // type () => number
Is there any possibility of preserving a generic call signature on the get handler?
interface Demo {
a: 1 | 2,
b: 3 | 4,
}
const getter = <T extends unknown>(value: T) => T;
const demo: Demo = {
a: getter( // cursor here
For this simple example, we get an autocomplete suggestion of 1 | 2 in order to satisfy the Demo interface, even though the argument is typed more loosely as unknown.
It would great to have this behavior when proxies are used to satisfy an interface with stricter typing than that of the get hander (in this case, the same autocomplete suggestion after typing getter[).
Most helpful comment
You're right, our current definition of
Proxyassumes that the output type is the same as the input type, when it could be any type, and just happen to want access totarget.Maybe we could do something like this: