Resolving TypeScript Generic Component Props Type Mismatch and Discriminated Union Issues
This article discusses a TypeScript typing problem where a generic component array fails to produce type errors for mismatched props, explains why the compiler reports missing properties from unrelated unions, and presents several refined type definitions that correctly enforce prop validation for Input and Select components.
When defining a function addComp that receives an array of component configurations, the author encountered a situation where TypeScript did not flag incorrect prop types, such as using a string[] for defaultValue of an Input component or adding an options field that does not belong to InputProps . The initial generic definition used a mapped type CompPropsMap[T] with a discriminated union on type , but the compiler treated the props field as a union of InputProps | SelectProps , causing it to check for properties required by the other branch (e.g., options ), which led to confusing error messages.
The author tried several alternative definitions:
// Initial interface definitions
interface CompProps {
required?: boolean;
placeholder?: string;
}
interface InputProps extends CompProps {
defaultValue?: string;
}
interface SelectProps extends CompProps {
defaultValue?: string[] | number[];
options: Array<{ label: string, value: string }>;
}
type CompPropsMap = {
Input: InputProps;
Select: SelectProps;
};
type CompType = 'Input' | 'Select';
interface AddCompParam
{
type: T;
props: CompPropsMap[T];
}With this definition, an example object like the following did not produce an error, even though defaultValue was an array and an unrelated options field was present:
// Example that should error but does not
const addCompParam: AddCompParam
[] = [
{
type: 'Input',
props: {
required: true,
defaultValue: ['默认值'], // should be string, not string[]
placeholder: '请输入',
options: [] // not part of InputProps
}
}
];TypeScript instead reported a missing options property because it inferred props could be SelectProps and therefore required options . To obtain accurate checking, the author explored a union‑type definition without generics:
// Union type approach
type AddCompParam =
{ type: 'Input'; props: InputProps } |
{ type: 'Select'; props: SelectProps };While this produced correct errors, it forced a fixed order of elements in the array, which was undesirable. The next attempt moved the discriminant type into the props object, but TypeScript still treated the two fields separately during inference.
Finally, a solution using a generic CompProps that includes the type field and a mapped type for the parameter array resolved the issue:
// Final solution with type‑in‑props and mapped type
interface CompProps
{
type: T; // bring the discriminant into props
required?: boolean;
placeholder?: string;
}
interface InputProps extends CompProps<'Input'> {
defaultValue?: string;
}
interface SelectProps extends CompProps<'Select'> {
defaultValue?: string[] | number[];
options: Array<{ label: string, value: string }>;
}
type CompPropsMap = {
Input: InputProps;
Select: SelectProps;
};
export type CompType = keyof CompPropsMap;
type AddCompParam = {
[K in CompType]: { type: K; props: CompPropsMap[K] }
}[CompType];
// Example using the new type
const addCompParam: AddCompParam[] = [
{
type: 'Input',
props: {
type: 'Input',
required: true,
placeholder: '请输入',
defaultValue: ['1'] // error: should be string
}
},
{
type: 'Select',
props: {
type: 'Select',
required: true,
placeholder: '请输入',
defaultValue: ['1'],
options: []
}
}
];This arrangement ties the type field to the corresponding props type, so TypeScript can correctly narrow the union and report precise errors such as a mismatched defaultValue type for Input or the presence of unexpected properties.
Community members contributed alternative implementations, including a generic function wrapper that enforces the same constraints:
// Generic function version
function addComp
(param: AddCompParam
) {
return param;
}
addComp({
type: 'Select',
props: {
required: true,
defaultValue: [8], // error: number[] not allowed for Select's string[]
placeholder: '请输入',
options: []
}
});All these solutions demonstrate how to achieve strict type safety for component configuration arrays in TypeScript by correctly coupling discriminant properties with their respective prop interfaces.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.