Reputation: 41
I have the following type definition:
type FuncWithRequiredParams = (a: number, b: number, c:string) => void
const func1: FuncWithRequiredParams = (a: number, b: number, c: string) => {} // works
const func2: FuncWithRequiredParams = (a: number, b: number, c: string, d: boolean) => {} // Error
const func3: FuncWithRequiredParams = (a: number, b: number) => {} // works, but I want to force it to fail
is there any workaround for this?
Upvotes: 1
Views: 290
Reputation: 369
First, let's be clear that this behavior is in fact correct. In this code:
type FuncWithRequiredParams = (a: number, b: number, c: string) => void;
const func3: FuncWithRequiredParams = (a: number, b: number) => {};
the conversation between you and the compiler looks like this:
TSC: Give me a function that I can call with three arguments.
You: Here's a function with two parameters.
TSC: Ok, I can call that with three arguments, your function will just ignore the third one. I'll take it.
And as caTS points out, usually that's what you want. If a function doesn't even use its third parameter, why type it in the first place?
Now, I assume you have a good reason for wanting this (maybe there's a callback that every implementing function has to always call, or something like that), and it's more about making implementers aware of accidental mistakes.
Here's one solution that might work for you. I imagine your callbacks are going to be passed to some function like registerPlugin
or addMiddleware
or something along those lines.
If you make that function generic, have the compiler infer the exact type of the callback being passed in, and ensure that the inferred type matches the expected one exactly, you have at least some assurance:
type FuncWithRequiredParams = (a: number, b: number, c: string) => void;
const func1 = (a: number, b: number, c: string) => {};
const func2 = (a: number, b: number, c: string, d: boolean) => {};
const func3 = (a: number, b: number) => {};
type Identical<A, B> = A extends B ? (B extends A ? A : never) : never;
function registerPlugin<T>(f: Identical<T, FuncWithRequiredParams>) {
// ...
}
registerPlugin(func1); // works
registerPlugin(func2); // error
registerPlugin(func3); // error
Just be aware that this is is anything but foolproof (and it can't be, because as I pointed out earlier, the original behavior is technically just fine).
For example, if you explicitly provide the type parameter, the compiler won't complain:
registerPlugin<FuncWithRequiredParams>(func3); // works
And if you explicitly set the type of func3
like you did in your original code:
const func3: FuncWithRequiredParams = (a: number, b: number) => {};
registerPlugin(func3); // works
it compiles as well.
Upvotes: 2