I’ve got this interface which will serve as props for a section component displaying different equipment. Each piece of equipment will be placed on a card inside a grid column.
Here’s the interface:
interface EquipmentSectionProps {
bgColor: "black" | "lightGrey";
columns: number;
category: string;
products: string[];
productImages: string[];
productReferals: string[];
}
Given the property columns, I want to restrict the size of the arrays products
, productImages
and productReferals
with the property columns
since their disposal is correlated with the number of columns.
The obvious first thing to try was to pass the property column inside the array directly like this:
interface EquipmentSectionProps {
bgColor: "black" | "lightGrey";
columns: number;
category: string;
products: string[columns];
productImages: string[columns];
productReferals: string[columns];
}
however, the response comes back as
Cannot find name 'columns'.ts(2304) type columns = /*unresolved*/ any
Is there an easier way to make this possible without creating a duplicated interface and passing the property into the new one?
2
Answers
Unfortunately you can’t restrict the length of an array via Typescript types (e.g. interface declarations like the one you’re showing). You’ll have to write the length checks inside the code for your component.
Even though there’s a way to initialize an array with a specific length in javascript, there’s no enforcement within javascript that can make the length immutable. Nothing is stopping the array from being lengthened, which means you need to check the length explicitly as-needed.
You can make the interface generic in the type of the
columns
property, and then define the corresponding array properties to be tuple types of the appropriate length.In order to do this you’d need a utility type like
TupLen<N extends number, V>
which will resolve to a tuple of lengthN
where every element is of typeV
:But there’s no built in
TupLen<N, V>
; you have to write your own. One way is as a tail-recursive conditional type using variadic tuple types:Also, if you don’t want to have to write the column length twice, as in,
const v: EquipmentSectionProps<3> = { columns: 3, ⋯}
(once for the type argument and once for thecolumns
property), you could instead write a helper function to infer the generic type argument from the value of thecolumns
property:You get the same behavior without needing to explicitly write out
EquipmentSectionProps<3>
.Playground link to code