microsoft / TypeScript Public
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
Exact generic constraints #35899
Comments
Why use generics when you are not using the type parameter? const hoc = (child: (arg: {a: string}) => void): void => {
child({ a: 'apple' });
}; |
@AnyhowStep The code sample was a reduced example. If you click through to the playground, it shows a more meaningful example where the type parameter is being used. Update: I updated the code sample to include use of the type parameter. |
I've got a workaround but it isn't the prettiest, |
You can have upper and lower bound constraints on a parameter |
Alternative solution, still requires a cast unfortunately. Playground // child() requires data including `a` and `b`.
function child(data: { a: string, b: string }): void {
console.log(data.a, data.b)
};
// Merge two objects, taking types from U if possible, and falling back to T
type Merge<T, U> = {
[K in keyof T | keyof U]: K extends keyof U ? U[K] : K extends keyof T ? T[K] : never
}
// Remove props in T that are also in U
type RemoveCommonProps<T, U> = Pick<T, Exclude<keyof T, keyof U>>
// hoc() supplies `a`, and returns a function that only requires `b`.
type Addition = { a: string };
function hoc<T extends Addition>(child: (data: Merge<T, Addition>) => void) {
function wrapped(data: RemoveCommonProps<T, Addition>) {
// Still requires a cast unfortunately
child({ ...data, a: 'apple' } as Merge<T, Addition>);
}
return wrapped;
}
const wrapped = hoc(child);
wrapped({ b: 'boy' });
wrapped({ b: 'boy', a: 'a' }) // Error, provided by hoc (freshness check)
const data = { a: 'a', b: 'b' }
wrapped(data) // No error because object literal is not fresh |
35899
…On Sun, Dec 29, 2019, 11:37 AM Gerrit Birkeland ***@***.***> wrote:
Alternative solution, still requires a cast unfortunately. Playground
<https://www.typescriptlang.org/play/index.html?ssl=1&ssc=1&pln=32&pc=62#code/PTAEGMAsEsBsBMAUBKUAnApgRwK7UwM6jwCGALiaNAHbiw7w0DmoABia6CdfGwEasAdACgAZjlploAe2oQYCRKQoAuUAG8uagmTTMANKD7bdzUAF9kagG7TovdcNDOIsgtNgZBsaUyXkSQRJDZUC+ZGFzAG5hYRBQAFkMNCYMUDIAd2lQaT4AKwxwMgJDCgBrMzIATwAHDCJRNGkAW1AAVSpRUBrpAgJoPk9Dbl5RElhYMz4ScDL07IAVYWq6xOTUgB4FwzaAPlAAXg0nFwBtAGkqOTKMKukuhdAAH1Abu662gF01S4wADzIGB4RDe93aoAA-O0Lp9QD9QP9AcDXrcwY8oQsYXDQNQMNZkpFYvEAEoYZrSfHdJo1Ig0UCPMiQchcTBcWDuK7tZa1NKk8n4gDCLXJ1AACtSCFsdvsjqLoLMpaAAKJ-OgMDAbUEPQxa9q7XZEsCQaTgFCgAg4Go1Sb1NgcYY8dAYMg4NDUIiUcSSGRyRnM2SwKpO3D4W2sAQiFZpACC8EYUlkhw0WnNpmoLGisS9RR9oGN4C2CIBQPgRFj8Z9u0QUDg8DU-lUaxSGu2oHL0AT1F2qAO+1s9lQjhcoGzndAGTQJCtGCQoTUfIpGCFzRF4ukNMV7c73eOw+H8QAylIJsG8IQuBASDpQBJRNI0C7qOQMIGTnv5LXEJpBD-QsM1AA5FO1oYABFhcEQSTNpucYdpWyAxMO5ixMOmAum646TtO8AxMhwjgG4ZCYcBM5Jvm1YKPACGxBOJFIJoxigABfDSFUYGWDEtHYV+RiASxbH-kxJDsag8RKmgTRoIYNRNNY9ikXwQb5qAiCNPUkC4n08iFGUET4YRxABEmmgkIBwmGIxzHscIXF1LOASiWAABy2TJJJRiFCQOAEGkuQFEUoCTICk6wFQRDUNIRFqQQkBAA>
// child() requires data including `a` and `b`.function child(data: { a: string, b: string }): void {
console.log(data.a, data.b)
};
// Merge two objects, taking types from U if possible, and falling back to Ttype Merge<T, U> = {
[K in keyof T | keyof U]: K extends keyof U ? U[K] : K extends keyof T ? T[K] : never
}
// Remove props in T that are also in Utype RemoveCommonProps<T, U> = Pick<T, Exclude<keyof T, keyof U>>
// hoc() supplies `a`, and returns a function that only requires `b`.type Addition = { a: string };
function hoc<T extends Addition>(child: (data: Merge<T, Addition>) => void) {
function wrapped(data: RemoveCommonProps<T, Addition>) {
// Still requires a cast unfortunately
child({ ...data, a: 'apple' } as Merge<T, Addition>);
}
return wrapped;
}
const wrapped = hoc(child);
wrapped({ b: 'boy' });wrapped({ b: 'boy', a: 'a' }) // Error, provided by hoc (freshness check)
const data = { a: 'a', b: 'b' }wrapped(data) // No error because object literal is not fresh
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#35899?email_source=notifications&email_token=AAXC6EL3FQWB7WVUBOVQQUDQ3DG4RA5CNFSM4KAZBLTKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHZDJVY#issuecomment-569521367>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAXC6ELJOFNJLW75SV2QXVLQ3DG4RANCNFSM4KAZBLTA>
.
|
I think @Gerrit0's solution is reasonable. If I understand correctly the errors (or lack-of) at the call to Expecting an error for a call like |
Tagging this |
I ran into this same issue, posted on SO, and was very surprised that the response was there's no way to require an exact match. +1 for this feature. |
I have similar but slightly more complex issue: #41930 I think I might have solved your problem while working on it. Take a look at my example and I must say it works as "extending" middleware. So that you can make it so: middleware(foo(), bar(), handler);
|
Search Terms
Suggestion
I would like to write a function that wraps a child function and provides a subset of its required arguments.
Here's an example: https://www.typescriptlang.org/play/index.html#code/PTAEGMAsEsBsBMAUBKUAnApgRwK7UwM6jwCGALiaNAHbiw7w0DmoABia6CdfGwEasAdACgAZjlploAe2oQYCRKQoAuUAG8uagmTTMANKD7bdzUAF9kagG7TovdcNDOIsgtNgZBsaUyXkSQRJDZUC+ZGFzAG5hYRBQSGlwFFACHAAHdNhoDCJ2VkNuXkwyHDRqIkpxSRk5MkhyUFlYAE90bDxCfiFhMhb0jFAAQXhGKVlQAF4NLVTTahZosQlwcblE8AAeABVQDAAPMgweIhGx2oA+RCg4eDV-VVBt1EmL0Ft7K1AHkjUAeQAttAyDtDABrDAtaSiYajYGXF5vD4OJwuaqrWqgADuaBImQwSFC-yBIO24Mh0Nh51kF1QjhcDPkt0QmkEbNChTUAHI8VkMFyLMgYgzzLEGSUynIcbyCTFRcJwG4yNjcfjeNMNtcFPAhcJpWqWUZuXxpC0BZYokA.
However, this errors out because the child function's generic argument
H
could be defined to be more specific than justa: string
(for example, it could be defined to bea: 'foo' | 'bar'
). This is covered here: #29049, and explained here: https://stackoverflow.com/a/56701587.It would be useful to have a way to set a constraint on the generic
H
where it must contain exactly the fields that it is "extend"ing from. For example:I don't fully understand why, but a similar example in Flow works without issue (possibly due to the use of exact types?): https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVxhgMYAsCWMAJgBQCUYATgKYCOArvjQM5hECGALu2PgHbYY9IvwDmYAAbsJYdnyKSARhIB0qKPQGd8cPjgLESHbgC4wAbwA+ss806UxAGjCLb9sWEsBfMmYBucPgK5qhgYTi6zHAw1CrwokZc7CrszsbJimSoXgDc6JhguHDY5GDM9AAOFTD41KxSEs5yCjSc9JR8rDwaWjp6nLhcYLowAJ5UdIwsSqrqmtjauoXFADwAKmbmNmXufOJeAHwkeIREZommYGsUALwHYAFBvmAX7GYAJAAi+FBQ685WbZ2Bx7TyHW73R7BULhHoLPpgBCUdhVaikdIfb6-f4WaxvHYg8TeA4UELhcn6U4kLYqWnpJpmADkKOq1EZYB8eXJXnQ5Na7T0SJZaLyPNQ2EinERyNRChuyxKJ2IZDyQtl1NcYEZijgo0ZnKAA.
Use Cases
My motivating use case is building a well-typed middleware framework. Middleware often add fields to the
context
of an HTTP request before or after the handler is called. I would like for a Middleware function to exactly specify which fields it adds to the handler. Applied to the playground code above,child
is the HTTP handler, andhoc
is one such middleware function.Examples
As above.
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: