Function, String Literal
Show code:
interface T {
(name: 'a', options: { key1: string }): string
(name: 'b', options: { key2: string }): string
(name: string, options?: any): string
}
const t: T = name => name
t('a')
// ^^^^ no error occurs
I know why there is no error. Typescript try to match the first overload. But no match. Then try to match second overload. Also not. But matched in the last overload. So there is no errors occur.
But I suggest typescript to throw error. I want function invoked with 'a' must have the second param {key1: string}, and invoked with 'b' must have the second param {key2: string}. The other string without any limited.
I write a i18n function to generate string. In some key, the second param cannot be ignored. And others is no limited.
function intl(key: 'X', value: {key: string}): string
function intl(key: string, value: any): string
intl('X') // error
intl('X', {key: 'aaa'}) // correct
In above.
My suggestion meets these guidelines:
I'm not sure I understand the suggestion; is it: consider each overload signature in turn until you find one where the first parameter matches, and then never consider any others even if subsequent parameters fail to match? In any case I don't think we can expect that the overload resolution algorithm will be altered to prohibit valid matches, which would likely break lots of real world TypeScript code.
Instead, I'd recommend that you find a way to communicate to the compiler what you're trying to accomplish. Probably for this I'd use a combination of generics, lookup types and rest tuples, instead of overloads:
interface Options {
a: [{ key1: string }],
b: [{ key2: string }],
[k: string]: [any?]
}
interface T {
<S extends string>(name: S, ...options: Options[S]): string;
}
const t: T = (name: any) => name
t('a', { key1: "a" }); // okay
t('b', { key2: "b" }); // okay
t('c'); // okay
t('c', 12345); // okay
t('a'); // error, expected 2 arguments, got 1
t('b', 12345); // error, 12345 not assignable to {key2: string}
Negated types (#29317), once they land, would probably help the use case in the OP:
interface T {
(name: 'a', options: { key1: string }): string
(name: 'b', options: { key2: string }): string
(name: string & not ('a' | 'b'), options?: any): string
}
"a" is a string so intl("a") is a valid call.
I'd recommend rethinking your API or something; there's nothing you can do to harden against e.g.
const s: string = "A".toLowerCase();
intl(s);
@jcalz It's works, Thanks
Most helpful comment
Negated types (#29317), once they land, would probably help the use case in the OP: