🔍 Search Terms
- destructured function parameters.
- contextual typing (?).
Not sure if I have the correct vocabulary.
Related issues :
✅ Viability Checklist
⭐ Suggestion
When inferring the type of a ({arg1, arg2}) => void callback, missing properties should be excluded from the inferred type.
Currently, TS infers it as ({arg1, arg2}: T) => void, with T being the constraint used during the inference.
I'd like the inferred type to be : ({arg1, arg2}: Pick<T, "arg1"|"arg2">) => void.
TS is able to properly infer the callback type when the whole argument is missing (i.e. () => void), so I guess my suggestion should also be technically possible ?
📃 Motivating Example
This can be useful to declare callbacks that must be compatible with a given context, while enabling their usage in other contexts, compatibles with the callbacks, not necessary with the original context.
Playground Link
type Bindings<CTX extends Record<string, any>> = {
[K in keyof CTX]: (ctx: CTX) => void; // <- in reality the type here is a little more complex.
}
// in reality we use B & Record<Exclude<keyof B, keyof T>, never> to exclude extra-keys.
function foo<T extends Record<string, any>, B extends Bindings<NoInfer<T>>>(p: {props: T, bindings: B}) {
return p;
}
const x = foo({
props: {
foo: 34, faa: "ok", fuu: "ok", fii: 42
},
bindings: {
fii:() => {}, // OK: () => void
foo:(a) => {}, // OK: (a: T) => void
faa:(args: {foo:number}) => {}, // OK: ({foo: number}) => void
fuu: ({foo}) => {} // NOK: ({foo}: T) => void / we want ({foo: number}) => void
}
})
// we could reuse it like :
foo({
props: {
...x.props,
fii: string // override a property with a completely different type.
},
bindings: {
...x.bindings,
fii: () => {} // binding may need to be overridden too.
}
})
💻 Use Cases
- What do you want to use this for?
Writing reusable callbacks.
- What shortcomings exist with current approaches?
- What workarounds are you using in the meantime?
We need to explicit the types when destructuring, e.g. faa: (args: {foo:number}) => {}).
A possible workaround could have been the following, however TS doesn't do a "per key" inference.
type Bindings<CTX extends Record<string, any>, KEYS extends Record<keyof CTX, keyof CTX>> = {
[K in keyof CTX]: (ctx: Pick<CTX, KEYS[K]>) => void; // <- in reality the type here is a little more complex.
}
An alternative solution would be to allow TS to be more lax with ({foo}: T) => void, e.g. allowing ({foo: ...}) calls, even if the argument doesn't respect the T constrainst.
🔍 Search Terms
Not sure if I have the correct vocabulary.
Related issues :
✅ Viability Checklist
⭐ Suggestion
When inferring the type of a
({arg1, arg2}) => voidcallback, missing properties should be excluded from the inferred type.Currently, TS infers it as
({arg1, arg2}: T) => void, withTbeing the constraint used during the inference.I'd like the inferred type to be :
({arg1, arg2}: Pick<T, "arg1"|"arg2">) => void.TS is able to properly infer the callback type when the whole argument is missing (i.e.
() => void), so I guess my suggestion should also be technically possible ?📃 Motivating Example
This can be useful to declare callbacks that must be compatible with a given context, while enabling their usage in other contexts, compatibles with the callbacks, not necessary with the original context.
Playground Link
💻 Use Cases
Writing reusable callbacks.
We need to explicit the types when destructuring, e.g.
faa: (args: {foo:number}) => {}).A possible workaround could have been the following, however TS doesn't do a "per key" inference.
An alternative solution would be to allow TS to be more lax with
({foo}: T) => void, e.g. allowing({foo: ...})calls, even if the argument doesn't respect theTconstrainst.