2

Is it possible to declare typing for a Typescript function with the following behavior:

declare const fun: HowToTypeThis


export const string_or_number_fun = fun<
  'num', number,
  'str', string
  >()

string_or_number_fun should accept either
string_or_number_fun('str', string)
or
string_or_number_fun('num', number)

string_or_number_fun('str', '') 
string_or_number_fun('num', 12) 

My attempts led me to this:

interface HowToTypeThis {

  <Name1 extends string, Type1,
    Name2 extends string, Type2>(): <N = Name1 | Name2>(_: N, __: N extends Name1
      ? Type1
      : N extends Name2
      ? Type2
      : never): any

}

TS compiler reports string_or_number_fun as of type:

 const string_or_number_fun: 
 <N = "num" | "str">(_: N, __: N extends "num" ? number : N extends "str" ? string : never) => any

and it looks ok

but i get :
[ts] Argument of type '""' is not assignable to parameter of type 'never'. [2345]
and
[ts] Argument of type '12' is not assignable to parameter of type 'never'. [2345]

and it doesn't either guard from typing an unexpected string as first argument

2 Answers 2

2

You can achieve what you are looking for by first declaring a mapping of your custom type names to the type that it represents and then using that as part of your function declaration. Something like:

interface TypeNameToType {
    num: number;
    str: string;
}

function fun<T extends keyof TypeNameToType>(type: T, value: TypeNameToType[T])
{
    return value;
}

Now if you try and call fun('num', 'thisisastring'); Typescript will get mad that you used 'num' but the second param is not a number.

Sign up to request clarification or add additional context in comments.

1 Comment

nice one, this solution doesn't follow my construct, though, however, looks like the mapping interface thing could be introduced, to make it neater
1

There must be a constraint for N type parameter, otherwise it will be widened to string, resulting in never coming from a conditional type.

N extends Name1 | Name2 is the only missing part in your code:

interface HowToTypeThis {

  <Name1 extends string, Type1,
    Name2 extends string, Type2>(): <N extends Name1 | Name2 = Name1 | Name2>(_: N, __: N extends Name1
      ? Type1
      : N extends Name2
      ? Type2
      : never) => any

}

2 Comments

Wow, wonder why I didn't try that! :) 1 q: I now tried <N extends Name1 | Name2 > instead of <N extends Name1 | Name2 = Name1 | Name2> and it seems to be ok too, any consideration about this ?
it seems that the the default value for N, = Name1 | Name2, is unnecessary here; normally its used when you want to have fallback value for the type parameter if it's not provided at the call site. Default is not needed if the compiler can infer correct type for N from argument values.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.