aleclofabbro
aleclofabbro

Reputation: 1699

Typescript string driven typings

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

Upvotes: 2

Views: 65

Answers (2)

artem
artem

Reputation: 51579

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

}

Upvotes: 1

casieber
casieber

Reputation: 7542

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.

Upvotes: 2

Related Questions