-
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
Allow currying of generics #30123
Comments
Here is what I already tried: Version Aconst getTransformerA =
<I extends {[key in K]: I[K]}, K extends keyof I> (key: K, value: I[K]) =>
(input: I) => input
// We now can use it like this
const transformerA1 = getTransformerA<Interface, 'numberKey'>( 'numberKey', 123 )
// This correctly gives a type error
const transformerA1 = getTransformerA<Interface, 'stringKey'>( 'stringKey', 123 )
// Problem:
//We verbosely have to pass the name of the string as a type argument Version Bconst getTransformerB = < K extends string, Value > ( key: K, value: Value ) =>
(input: {[key in K]: Value}) => input
// Now this works which is much easier
const transformerB1 = getTransformerB( 'numberKey', 123 )
// But this gives a false positive, since we haven't defined which interface we are relating to
// It is to generic.
const transformerB1 = getTransformerB( 'stringKey', 123 )
// Problem:
// False positives, so no guarantee to be compatible with a certain interface. Version Cconst getTransformerC = <I extends {[key: string]: any}>() =>
<K extends keyof I>(key: K, value: I[K]) =>
(input: I) => input
// Works all as expected
const transformerC1 = getTransformerC<Interface>()('otherKey', 123)
const transformerC2 = getTransformerC<Interface>()('someKey', 123)
// Problem:
// We changed the runtime. Now we have an unnecessary function call. I also tried to use default values for type parameters, but couldn't get anything to work, that was improving on the above versions. |
I'm not sure I follow your example. What is the proposed type of I think this might be related, although not a duplicate: #29043 |
What do you intend to do with |
@jack-williams yes I agree, looks related even though its not exactly the same. Here is an example with a function, that actually does something so maybe it becomes more clear: interface Interface {numberKey: number, stringKey: string}
const instance: Interface = {numberKey: 1, stringKey: "a"}
const getTransformer =
<I><K extends keyof I>(key: K, newValue: I[K]) => // - Take a key name and a value.
(input: I) => // - Take an object.
Object.assign({}, input, {[key]: newValue}); // - Return a new object, with the
// new value assigned to the key;
const transformerA1 = getTransformer<Interface>( 'numberKey', 123 );
// transformerA1 now takes any Object that implements Interface,
// and changes the key "numberKey" to the value 123;
const newInstance = transformerA1( instance );
instance.numberKey // 1
newInstance.numberKey // 123
// If we try to build a transformer like this, we should get a type error
const transformerA2 = getTransformer<Interface>( 'stringKey', 123 );
// Expected Error:
// Argument of type '123' is not assignable to parameter of type 'string'.ts(2345) Note this is written with the syntax that is currently not working Hope everything else was covered in the opening post. |
Thanks for the expanded example. I think I understand your aim, though I'm not really clear on what difference this gives you vs #26349 (partial type inference with sigil). const getTransformer = <I,K extends keyof I>(key: K, newValue: I[K]) => (input: I) => ...
const transformerA1 = getTransformer<Interface,_>( 'numberKey', 123 );
const transformerA1 = getTransformer<Interface>( 'numberKey', 123 ); // or this, if the syntax filled in _ It seems like this does the same thing, more-or-less. Given that you apply the function immediately you never really materialise the partially applied generic, so it seems breaking it apart doesn't help much. I might be wrong on this though. If you had higher-kinded types then I think this proposal would be notably different to partial type inference. |
This would indeed almost be the same. The main difference to what I was looking for was the function making it a bit more clear which parameters should be set (the interface) for the function to properly work, while the other two parameters should never have to be set, because there should be almost not case where it would be necessary to set them. Like I said, the main advantage of the whole construct is, to have kind of type-saftey in cases, where the typying will be lost - meaning we want to ensure the function is compatible with an interface, before we call it (otherwise we could just go for Version B). Leaving the underscores there would definitely work and not kill anyone, but neither would repeating the string (Version A). But non of these version seem really descriptive to me, about how the function is supposed to be used. The definition of K ist just in the type parameters because thats where we define generics. Not because we want it to be a parameter. So maybe thats also the real issue here - Type parameters seem to serve two usecases (1. allowing configuration, 2. allowing interference), making it hard to see as a user of a function how it is meant to be used. |
Before finding this, I have tried to tack this very idea onto #26242 because I think both try to solve overlapping issues and making them them work hand in hand would be worthwhile. @essenmitsosse and others interested in this feature may want to chime in soon since #26242 has recently been added to the project team's agenda. |
Agree this is basically a duplicate of #26242 |
First thing: I know there are a lot of question and good answers about how to write, curried functions. What I am requesting here is to be able to curry generics.
This seems related to #10571, but I thing its a slightly different proposal.
For this example we use this interface and this simple Instance
What is expected
It would be greate to be abled to write a function like this
The advantage of this, is that we get a return function, that is guaranteed to work with a certain interface, before we call the return function. Which is especially useful in cases, where we could loose typing, but still want to ensure compatibility with an interface.
But this syntax doesn't work and I seem unable to find a workaround.
The text was updated successfully, but these errors were encountered: