Как я могу правильно вывести тип возвращаемого значения на основе функции с одним параметром, представляющим собой объединение двух типов?
Я пробовал следующее с условными типами, но это не работает (см. встроенный комментарий для ошибки машинописного текста):
type Status = 'statusType'
type GoalActivity = 'goalActivityType'
type Argument = { type: 'status'; status: Status | null } | { type: 'goalActivity'; goalActivity: GoalActivity | null }
const handleReaction = (arg: Argument): Argument extends { type: "status" } ? Status : GoalActivity => {
if (arg.type === 'status') {
return 'statusType' // Type '"statusType"' is not assignable to type '"goalActivityType"'.
} else {
return 'goalActivityType'
}
}
Я также пробовал следующее, используя форму перегрузки функций для стрелочных функций (, как описано здесь ), но это также приводит к ошибке TypeScript, а также использует «любой», который теряет большую часть преимуществ ввода внутри определения функции. :
type Status = 'statusType'
type GoalActivity = 'goalActivityType'
type HandleReaction = {
(arg: { type: 'status'; status: Status | null }): Status
(arg: { type: 'goalActivity'; goalActivity: GoalActivity | null }): GoalActivity
}
const handleReaction: HandleReaction = (arg: any) => { // Type '"goalActivityType"' is not assignable to type '"statusType"'.
if (arg.type === 'status') {
return 'statusType'
} else {
return 'goalActivityType'
}
}
Этот вопрос похож на этот, но с той разницей, что параметр функции является объектом.
Во-первых, вы не использовали универсальный тип для своего аргумента, в результате чего typescript
никогда не выведет правильный тип на основе вашего ввода (вы можете представить, что общий тип является параметром, tsc
требует, чтобы он вычислял результат на основе вашего ввода) .
Суммируя,
const handleReaction = (arg: Argument): Argument extends { type: "status" } ? Status : GoalActivity => { // ... }
всегда будет возвращать Status | GoalActivity
как возвращаемый тип.
Конечно, вы должны использовать общий тип здесь в качестве аргумента. Я разделю ваш код со встроенным объяснением:
type Status = 'statusType'
type GoalActivity = 'goalActivityType'
type StatusObj = { type: 'status'; status: Status | null };
type GoalActivityObj = { type: 'goalActivity'; goalActivity: GoalActivity | null }
type Argument = StatusObj | GoalActivityObj;
// Define returned type based on a input argument `T`
type ReturnType<T> = T extends StatusObj ? Status : GoalActivity;
// Generic type should be used here
const handleReaction = <T extends Argument>(arg: T): ReturnType<T> => {
if (arg.type === 'status') {
// Q: Why do we have to cast here?
// A: Any returned type can't assign to statement of `type ReturnType<T> ...`
// but luckily `tsc` still allows us to cast back since they are all string literal
return 'statusType' as ReturnType<T>;
} else {
return 'goalActivityType' as ReturnType<T>;
}
}
Ваши объекты-аргументы должны иметь свои собственные типы. Как только они это сделают, вы можете использовать перегрузку функций следующим образом:
type Status = { type: 'status', status: 'statusValue' };
type GoalActivity = { type: 'goalActivity', goalActivity: 'goalValue' };
function handleReaction(arg: Status): Status['status']
function handleReaction(arg: GoalActivity): GoalActivity['goalActivity']
function handleReaction(arg: Status | GoalActivity) {
if ('status' in arg) {
return arg.status;
} else {
return arg.goalActivity;
}
}
const a = handleReaction({ type: 'status', status: 'statusValue' })
// ^? 'statusValue'
const b = handleReaction({ type: 'goalActivity', goalActivity: 'goalValue' })
// ^? 'goalValue'
А, кажется, я понимаю. Жаль, что нам нужно привести возвращаемое значение, но я предполагаю, что это ограничение TypeScript.