Typescript: function overloads with string literal and other params no errors report.

Created on 24 May 2019  路  4Comments  路  Source: microsoft/TypeScript

Search Terms

Function, String Literal

Suggestion

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.

Use Cases

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

Examples

In above.

Checklist

My suggestion meets these guidelines:

  • [ ] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [ ] This feature would agree with the rest of TypeScript's Design Goals.
Working as Intended

Most helpful comment

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
}

All 4 comments

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

seanzer picture seanzer  路  3Comments

blendsdk picture blendsdk  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

Antony-Jones picture Antony-Jones  路  3Comments

jbondc picture jbondc  路  3Comments