GaryO
GaryO

Reputation: 6338

Typescript generics: how to ensure function args match each other?

I want to have a function with a arg of a certain type, and "matching" literal. How can I do that?

class T1 {
  val: string = "abc"
}
class T2 {
  val: number = 123
}
type TType = 'T1' | 'T2'

// I want to make this generic so the args have to "match"
function foo(ttype: TType, arg: T1 | T2) {
  console.log(`${ttype}: arg.val`)
}

foo('T1', new T1)
foo('T2', new T2)
foo('T1', new T2) // I want this to be a compile-time error

I'd like the code to require that when 'T1' is passed as ttype, an object of type T1 is required as the second arg and vice versa. I don't want two versions of the function if I can avoid that (don't want to copy the body) -- just a type spec that says "either literal T1 and a T1, or literal T2 and a T2". I'm happy to set up the mappings manually -- I don't need to get the class name from the string literal. Just don't know how to approach this in Typescript.

Upvotes: 0

Views: 95

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250106

The simples approach is to use overloads

function foo(ttype: "T1", arg: T1): void
function foo(ttype: "T2", arg: T2): void
function foo(ttype: TType, arg: T1 | T2) {
  console.log(`${ttype}: arg.val`)
}

Playground Link

You could also go with a generic function with a mapping interface, but for the example that would probably be overkill:

type TMap = {
  "T1": T1
  "T2": T2
}
// I want to make this generic so the args have to "match"
function foo<T extends TType>(ttype: T, arg: TMap[T]): void
function foo(ttype: TType, arg: T1 | T2) {
  console.log(`${ttype}: arg.val`)
}

Playground Link

Upvotes: 4

Related Questions