The generic type parameter R
in the Props
type and the Component
function is not helping you. Inside of Component()
you are trying to check relationType
to narrow callback
, but generic type parameters like R
are never narrowed via control flow analysis in TypeScript. See microsoft/TypeScript#13995 for more information.
Instead it would be best to just use a non-generic Props
of a discriminated union type, equivalent to your original Props<RelationType.one> | Props<RelationType.many>
:
type Props = { [R in RelationType]: {
callback(newValue?: R extends RelationType.one ? string : string[]): void;
relationType: R;
} }[RelationType];
/* type Props = {
callback(newValue?: string | undefined): void;
relationType: RelationType.one;
} | {
callback(newValue?: string[] | undefined): void;
relationType: RelationType.many;
} */
In the above I've had the compiler calculate that union programmatically by mapping your original Props<R>
definition over RelationType
to form an object type whose properties I immediately look up.
Then your Component()
function can use a parameter of type Props
. Another caveat is that you cannot destructure it into relationType
and callback
inside the implementation signature if you want the compiler to keep track of the relationship between the two values. TypeScript doesn't have support for what I've been calling correlated union types (see microsoft/TypeScript#30581). You need to keep both values as properties of a single props
parameter if you want the behavior you're looking for:
const Component = (props: Props) => {
return (
<button onClick={() => {
if (props.relationType === RelationType.one) {
props.callback('foo');
} else {
props.callback(['foo', 'bar']);
}
}} />
)
};
That works as desired, I think.
Playground link to code
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…