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"
}
};
This is not possible.
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.
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.
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.
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.
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With