Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript function whose parameter should accept one of two types

Tags:

typescript

I am trying to create a function whose parameter can be one of two types. The function then may invoke another function. I am, however, getting an error message. You can see the script and a screenshot below. How do I write this function so that it can accept a parameter with one of two types? Thanks!

interface A {
  a: string;
  title: string,
}

interface B {
  b: number;
  title: string;
}

const alpha = (data: A) => data.a;

const beta = (data: B) => data.b;

const foo = (data: A | B) => {
  if (data.title === 'a') {
    return alpha(data);  // Message: "Argument type A | B is not assignable to type A"
  }
  if (data.title === 'b') {
    return beta(data);  // Message: "Argument type A | B is not assignable to type B"
  }
};

enter image description here

like image 220
Jimmy Avatar asked Oct 03 '19 20:10

Jimmy


People also ask

Can you pass a type as a parameter TypeScript?

This is not possible.

How do you call a function with parameters in TypeScript?

In functions, parameters are the values or arguments that passed to a function. The TypeScript, compiler accepts the same number and type of arguments as defined in the function signature. If the compiler does not match the same parameter as in the function signature, then it will give the compilation error.

How do I create a return type function in TypeScript?

To define the return type for the function, we have to use the ':' symbol just after the parameter of the function and before the body of the function in TypeScript. The function body's return value should match with the function return type; otherwise, we will have a compile-time error in our code.


3 Answers

Try using type assertions (like casting) on the parameter so the compiler (and IDE) treats it as the appropriate type once you've determined which type it is.

interface A {
  a: string;
  title: string,
}

interface B {
  b: number;
  title: string;
}

const alpha = (data: A) => data.a;

const beta = (data: B) => data.b;

const foo = (data: A | B) => {
  if (data.title === 'a') {
    return alpha(<A>data);
  }
  if (data.title === 'b') {
    return beta(<B>data);
  }
};

Type assertions are a way to tell the compiler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler. TypeScript assumes that you, the programmer, have performed any special checks that you need.

like image 179
Tom Faltesek Avatar answered Oct 01 '22 18:10

Tom Faltesek


The issue is that typescript does not know that title === 'a' means that the type for data is a. Luckily, you can provide a more narrow type instead of string to help typescript with that.

interface A {
  a: string;
  title: 'a' ; // title can now be 'a'  only, not any other string. 
}

interface B {
  b: number;
  title: 'b' ; // title can now be 'b'  only, not any other string. 
}
like image 35
pascalpuetz Avatar answered Oct 01 '22 17:10

pascalpuetz


You need a type guard, something that would tell TS that A objects have their title equal to a, and B objects - to b:

const isA = (x: A | B): x is A => x.title === 'a';
const isB = (x: A | B): x is B => x.title === 'b';

and then:

const foo = (data: A | B): any => {
  if (isA(data)) {
    return alpha(data);
  }
  if (isB(data)) {
    return beta(data);
  }
};

A better option would be to make your discriminating value (a and b) a type rather than a string:

interface A1 {
  a: string;
  title: 'a',
}

interface B1 {
  b: number;
  title: 'b';
}

Then you can simply use switch, which acts as type guard:

const foo1 = (data: A1 | B1): any => {
  switch (data.title) {
    case 'a': return alpha(data);
    case 'b': return beta(data);
  }
};

Docs

PG

like image 38
georg Avatar answered Oct 01 '22 16:10

georg