-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Method signatures containing references to properties of constrained generic types are resolving through the constraint instead of the supplied type argument #12651
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
Comments
A convenient way to illustrate this is that: type MethodDescriptor = {
name: string;
args: any[];
returnValue: any;
} Represents a set of base constraints for a compile-time "object-like" entity containing named type arguments, e.g.
And: type SomeMethodDescriptor = {
name: "someMethod";
args: [number, string];
returnValue: string[];
} Represents an "instance" or "implementation" of the Now the function signature: function dispatchMethod<M extends MethodDescriptor>(name: M['name'], args: M['args']): M['returnValue']; References the types of properties on the type argument And the function call: let result = dispatchMethod<SomeMethodDescriptor>("someMethod", ["hello", 35]); Passes Note that in this example both the types I know this may be seen as somewhat abstract so I hope this helps.. |
In combination with discriminated unions, it might be possible to encapsulate the dispatcher in a class such that only one "type schema" is actually needed to be supplied once during construction to describe all supported actions: type MethodDescriptor = {
name: string;
args: any[];
returnValue: any;
}
class Dispatcher<M extends MethodDescriptor> {
dispatch(name: M['name'], args: M['args']): M['returnValue'] {
// ...
}
}
type StoreMethodDescriptor = {
name: "store";
args: [string, number[], boolean];
returnValue: void;
}
type OpenMethodDescriptor = {
name: "open";
args: [string];
returnValue: number;
}
const dispatcher = new Dispatcher<StoreMethodDescriptor | OpenMethodDescriptor>();
// The correct argument types and return type are inferred and validated automatically
// using the string literal as discriminant:
dispatcher.dispatch("store", ["mydata.txt", [1, 2, 3, 4], true]); |
I realized there's a subtle assumption I introduced here in my discriminated union example. Consider this case: type MyType = { a: string; b: number } | { a: boolean; b: string[] };
function test(x: MyType['a'], y: MyType['b']) {}
test("hi", ["a", "b", "c"]); // <- currently doesn't error Here, Is this the right way to go? maybe? though the case I described was slightly different: class Test<T extends { a: any; b: any }> {
test(x: T['a'], y: T['b']) {
// ...
}
}
const instance = new Test<{ a: string; b: number } | { a: boolean; b: string[] }>();
instance.test("hi", ["a", "b", "c"]); // <- Should this error? Should both class Test<T extends { a: any; b: any }> {
test<M uniquely extends T>(x: M['a'], y: M['b']) {
// ...
}
}
const instance = new Test<{ a: string; b: number } | { a: boolean; b: string[] }>();
instance.test("hi", ["a", "b", "c"]); // <- Errors as M is constrained not to be a union The suggested keyword might not prove to be that great though, I'm not sure.. perhaps there's a better one, or a different approach altogether.. |
A simpler alternative to class Test<T specifies { a: any; b: any }> {
test(x: T['a'], y: T['b']) {
// ...
}
}
const instance = new Test<{ a: string; b: number } | { a: boolean; b: string[] }>();
instance.test("hi", ["a", "b", "c"]); // <-- Errors due to the semantics of "specifies". Although Another advantage of |
The issue described in the original post is now fixed by #12770. |
Thanks! Maybe I'll try to somehow apply the patch tomorrow (or wait until it's in the nightly build) and see if I can start making use of this new inference capability in my code. If you find any of these additional ideas I mentioned here interesting feel free to make use of them in the future. They were originally meant as motivating examples. I'm not sure, though, whether I'm in a good position to figure out a whole proposal from them at this point. It would be interesting, perhaps, to first see what other people would do / want to do with these inference patterns and then try to come to some form of understanding what's the 'best' way to approach this. |
TypeScript Version: nightly (2.2.0-dev.20161203)
I currently find it somewhat hard to precisely describe the issue/suggestion, but I believe this use case should demonstrate it clearly:
The general idea here is that the types are used unconventionally: not to describe interfaces to run-time objects but as a way to "pack" a set of type arguments into an "object-like" compile-time entity. Perhaps this could be called something like "packed type arguments" or "type argument classes" or maybe "type argument schema containers"? I'm not sure.
Anyway, I was trying to write a general purpose dispatcher that can invoke a set of actions, and that seemed like a more elegant and type safe alternative to the more conventional:
The text was updated successfully, but these errors were encountered: