'Typescript dynamic type based on the parent functions parameter enum value
I am trying to conditionally define a child functions parameter type based on a enum variable obtained from the parent functions parameter value.
It seems that when a function parameter value is used to conditionally define a child functions parameter type, typescript is unable to understand the condition.
See example:
enum PaymentMethodTypes {
CARD = "card",
DIRECT_DEBIT = "direct_debit",
}
type Card = {
type: PaymentMethodTypes.CARD
}
type DirectDebit = {
type: PaymentMethodTypes.DIRECT_DEBIT
}
const Component = ({ paymentType }: {paymentType: PaymentMethodTypes}) => {
type PaymentMethod = typeof paymentType extends PaymentMethodTypes.CARD ? Card : DirectDebit;
const method = (paymentMethod: PaymentMethod) => {
console.info(paymentMethod.type)
if (paymentType === PaymentMethodTypes.CARD) {
console.info(paymentMethod.type === PaymentMethodTypes.CARD);
// ERROR: This condition will always return 'false' since the types 'PaymentMethodTypes.DIRECT_DEBIT' and 'PaymentMethodTypes.CARD' have no overlap.
}
}
}
When a variable is defined, rather than via the function parameter or a conditional value, Typescript is able to understand the type condition correctly.
See example:
enum PaymentMethodTypes {
CARD = "card",
DIRECT_DEBIT = "direct_debit",
}
type Card = {
type: PaymentMethodTypes.CARD
}
type DirectDebit = {
type: PaymentMethodTypes.DIRECT_DEBIT
}
const paymentType = PaymentMethodTypes.Card;
const Component = () => {
type PaymentMethod = typeof paymentType extends PaymentMethodTypes.CARD ? Card : DirectDebit;
const method = (paymentMethod: PaymentMethod) => {
console.info(paymentMethod.type)
if (paymentType === PaymentMethodTypes.CARD) {
console.info(paymentMethod.type === PaymentMethodTypes.CARD);
// This now works
}
}
}
Any suggestions to resolve this would be greatly appreciated!
Solution 1:[1]
You'll have to use a generic parameter to obtain the narrowed type of paymentType.
See TS Playground for why this is
const ComponentFix = <T extends PaymentMethodTypes>({ paymentType }: {paymentType: T}) => {
type PaymentMethod = T extends PaymentMethodTypes.CARD ? Card : DirectDebit;
const method = (paymentMethod: PaymentMethod) => {
console.info(paymentMethod.type)
if (paymentType === PaymentMethodTypes.CARD) {
console.info(paymentMethod.type === PaymentMethodTypes.CARD);
}
}
}
In short the typeof operator returns the union of all possible values, which means it will actually be evaluating
type PaymentMethod = PaymentMethodTypes.CARD | PaymentMethodTypes.DirectDebit extends PaymentMethodTypes.CARD ? Card : DirectDebit;
Which always fails, hence it defaults to DirectDebit
EDIT: From comments discussion, The TS engine isn't able to infer type discrimination across function boundaries, instead you should seek to purify your function, see on playground
const ComponentFix = <T extends PaymentMethodTypes>({ paymentType }: {paymentType: T}) => {
type PaymentMethod = T extends PaymentMethodTypes.CARD ? Card : DirectDebit;
const method = (paymentMethod: PaymentMethod) => {
console.info(paymentMethod.type)
if (paymentMethod.type === PaymentMethodTypes.CARD) {
console.info(paymentMethod.type === PaymentMethodTypes.CARD);
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
