Reputation: 511
I'm trying to make a functional component in react which accepts two separate strings as props. Is there a way to make sure that these two arrays must have the same length, using TypeScript? In other words, when we are using this component inside other components, unless the props are the same length, we will receive an error.
To give you a simple example, imagine something like this:
export const TableView = ({ s1, s2 }: { s1: string[]; s2: string[] }) => {
return (
<table>
<thead>
<tr>
{s1.map((s, index) => {
return <th>{s1[index]}</th>;
})}
</tr>
</thead>
<tbody>
<tr>
{s2.map((s, index) => {
return <td>{s2[index]}</td>;
})}
</tr>
</tbody>
</table>
);
};
I want to make sure I see an error when I use it with strings having different length.
<TableView s1={["1", "2", "3"]} s2={["1", "2", "3", "4"]} />
{/* show an error here */}
Upvotes: 3
Views: 1177
Reputation: 328788
In order to make the compiler keep track of the lengths of the s1
and s2
elements of the input parameter, you should make TableView
generic in the type of s1
, and then use that to check s2
. The call signature for TableView
should therefore look something like this:
<T extends string[]>({ s1, s2 }: {
s1: [...T];
s2: [...{ [I in keyof T]: string; }];
}) => JSX.Element
Here we are saying that T
is some array type of strings, and that s1
is essentially of type T
. The syntax [...T]
uses variadic tuple type syntax to give the compiler a hint that we'd like it to interpet T
as a tuple type and not just an array type. Tuple types have a known order and length, while plain array types do not. You care about the length, so you want a tuple type.
It would be nice if we could write [...T]
for s2
also, but unfortunately for you, the compiler will tend to infer the elements of T
as being string literal types like "1"
and "2"
instead of string
. If so, saying that s2
is [...T]
would be placing the restriction that each element of s2
would have to be exactly equal to the corresponding element of s1
. We don't want to reject {s1: ["1", "2"], s2: ["2", "1"]}
, so we can't do that.
So instead we say that s2
is a mapped tuple type with the same number of elements of s1
but where each value type can be a string
.
That's the constraint you want.
Let's test it:
<TableView s1={["1", "2", "3"]} s2={["4", "3", "2", "1"]} /> // error!
// --------------------------➨ ~~
// Source has 4 element(s) but target allows only 3.
<TableView s1={["1", "2", "3"]} s2={["4", "3", "2"]} /> // okay
Looks good!
Upvotes: 5
Reputation: 132
I think you can use something like below before return
if(sl.length !== s2.length) {
throw "Arrays are of different length"; //which throws custom exception
/*Or you can return anything that suits your needs*/
}
return(
//here goes your code
)
Upvotes: 0
Reputation: 722
You can simply put the condition in your jsx like so :
export const TableView = ({ s1, s2 }: { s1: string[]; s2: string[] }) => {
return (
{s1.length === s2.length ?
<table>
<thead>
<tr>
{s1.map((s, index) => {
return <th>{s1[index]}</th>;
})}
</tr>
</thead>
<tbody>
<tr>
{s2.map((s, index) => {
return <td>{s2[index]}</td>;
})}
</tr>
</tbody>
</table>
:
<p> Error arrays are not the same length ...</p>
}
);
};
Upvotes: 0