I'm trying to type properties of the React component in a way that when one of the properties has specific value then the type of the other property changes. Overall, I know how to do it if the input type is something like string | number
but I don't know how to do it if it's string | "specificString"
. Maybe it's not even possible?
Here are my two attempts to achieve that.
type Action = "createItem" | "updateItem" | "deleteItem";
type Props<TName = string> = {
name: TName;
value: TName extends "_action" ? Action : string;
};
const props: Props = {
name: "_action",
value: "wrongAction", // I want it to error
};
Second try:
type Action = "createItem" | "updateItem" | "deleteItem";
type Props = {
name: "_action";
value: Action;
} | {
name: string;
value: string;
};
const props: Props = {
name: "_action",
value: "wrongAction", // I want it to error
};
Any ideas how to achieve that? Thanks!
CodePudding user response:
See this example:
import React from 'react'
type Action = "createItem" | "updateItem" | "deleteItem";
type ValidateProps<T> =
/**
* Check whether our argument has allowed type shape
*/
T extends { name: infer A, value: infer V }
/**
* Check if [name] is "_action"
*/
? ('_action' extends A
/**
* If [name] is "_action" and [value] is Action
*/
? (V extends Action
/**
* Return T
*/
? T
/**
* Otherwise highlight wrong property
*/
: { name: A, value: 'Error: [value] is not allowed' })
: { name: A, value: V }
)
: never
type Props<Name, Value> = {
name: Name,
value: Value
}
const Foo = <
Name extends string,
Value extends string,
>(props: ValidateProps<Props<Name, Value>>) => <div />
const allowed1 = <Foo name="_action" value="createItem" /> // ok
const allowed2 = <Foo name="anything" value="hello" /> // ok
const not_allowed = <Foo name="_action" value="hello" /> // expected error
You need to infer literal type from name
and value
property and them validate it.
If you are interested in argument type inference, you can check my articles here and here
P.S. Feel free to use never
instead of 'Error: [value] is not allowed'
. I just added it for the sake of readability