-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typescript can't infer types when using Proxy #20846
Comments
You're right, our current definition of 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:
So the idea of 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...
|
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 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 It would great to have this behavior when proxies are used to satisfy an interface with stricter typing than that of the |
Well its 2022 now. Any updates on this? :) |
This was an unintended side effect of moving to script-mode TypeScript. Unfortunately, TS doesn't handle Proxies very well and thinks that these are undefined (which is technically true until the proxy is called.) TypeScript will hopefully make typing proxies better in the future: microsoft/TypeScript#20846
Optionality here was an unintended side effect of moving to script-mode TypeScript. Unfortunately, TS doesn't handle Proxies very well and thinks that these are undefined (which is technically true until the proxy is called.) TypeScript will hopefully make typing proxies better in the future: microsoft/TypeScript#20846
First of all, any update on this in 2023, and is there any way we can help get something over the line? Not an update on the issue just a couple of examples of how I've overcome this in the past, in case they can be of use to anyone else!. It's often a case by case task of tricking typescript into calling the proxy! Bear in mind the proxy examples below still technically require definitions of your keys and what types they return! So it's still not behaving the way a proxy would in vanilla Javascript! First example:// typescript needs to know that it can access any string property and that the prop should return our function
interface IFuncHash {
[element: string]: (t: TemplateStringsArray) => string;
}
class CallableObj extends Function {
constructor() {
super();
return new Proxy(this, {
apply: (target, prop, args: any[]) => target._call(...args),
get: (target, prop) => {
return (input: TemplateStringsArray) => {
return input.raw.join(" ");
};
},
});
}
_call(...args: any[]) {
return (args[0] as TemplateStringsArray).raw.join(" ");
}
}
const db = new CallableObj() as CallableObj & IFuncHash;
console.log(db`my styles here`); // We can call the object no problem!
console.log(db.div`div styles here`); // We can call methods that don't exist in our class via the proxy!
console.log(db.input`input styles here`); Second Example:function fakeBaseClass<T>(): new () => Pick<T, keyof T> {
return class {} as any;
}
// We need to trick TS into thinking our class "contains" the array methods without extending it
class ArrayExt<T> extends fakeBaseClass<Array<any>>() {
private array: T[];
constructor(array: T[]) {
super();
this.array = array;
return new Proxy(this, {
get(target, property) {
const exists = target[property as keyof typeof target];
if (exists !== undefined) return exists;
const underlyingAtt =
target.array[property as keyof typeof target.array];
if (typeof underlyingAtt === "function") {
return underlyingAtt.bind(target.array);
}
return target.array[property as keyof typeof target.array];
},
});
}
last(): T {
return this.array[this.array.length - 1];
}
}
const array = [1,2,5,1,2,5];
const ext = new ArrayExt(array);
console.log(ext.map((item) => item + 1)); // We can call the underlying Array.prototype.map via our proxy!
console.log(ext.last()); // We can still call members of ArrayExt<T>! |
Hey, I would actually really love to have this feature as well. Can we get some feedback on this, please? |
The type Data = {
aRecord?: Record<string, unknown>,
aString: string,
aNumber?: number,
};
const datum: Data = {
aRecord: { key: "value" },
aString: "string",
}
const proxy = new Proxy(datum, {
set(data, key: keyof Data, value) {
// Type 'any' is not assignable to type 'never'
data[key] = value;
return true;
}
}) |
Code
Expected behavior:
I would expect that when I type
prox.prop1.
, I would get typescript suggestions forNumber.prototype
, but instead, I get suggestions forFunction.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.
The text was updated successfully, but these errors were encountered: