#javascript #typescript
#javascript #typescript
Вопрос:
Если у меня есть следующее объединение, которое обертывает функцию и ее аргумент, как я могу его вызвать?
type Wrapper = {
fn: (a: string) => void
arg: string
} | {
fn: (a: number) => void
arg: number
}
let foo!: Wrapper
foo.fn(foo.arg) // Error Type 'string' is not assignable to type 'never'.
Я не могу понять, как это назвать. Все, что я пробовал, по сути, сводится к приведению (например, приведению к (a:any) => void
), которое я могу сделать, если нужно, но я чувствую, что мне не нужно этого делать.
Можно ли вызвать эту функцию без приведения?
Редактировать: Чтобы уточнить, я спрашиваю, существуют ли решения, которые не требуют изменения определения Wrapper
.
Комментарии:
1. Проблема в том, что, учитывая
type F = (x: A) => T
иtype G = (x: B) => U
, объединениеtype H = F | G
является(x: A amp; B) => T | U
. Чтобы вызвать функцию типаH
, нам нужно будет указать аргумент, который является подтипом обоихA
иB
.2. @AluanHaddad Да, похоже, это логика typescript, но она явно теряет информацию о типе. Я хочу знать, есть ли способ не потерять информацию о взаимосвязи между
fn
иarg
.3. Язык не считается
arg
дискриминантом . Вам нужно будет ввести дискриминант, то есть ссылку на игровую площадку
Ответ №1:
Вы можете использовать условный тип для устранения неоднозначности типов. К сожалению, это требует, чтобы вы в основном снова дублировали свои типы объединения оболочек, и если у вас есть еще что-то, что вам нужно проверить, ваш условный тип станет довольно уродливым. Но это позволяет вам избежать приведения (вроде).
type Wrapper = {
fn: (a: string) => void
arg: string
} | {
fn: (a: number) => void
arg: number
}
type Disambiguate<W extends Wrapper> = W['arg'] extends string
? { fn: (a : string) => void, arg: string }
: { fn: (a: number) => void, arg: number };
let foo!: Disambiguate<Wrapper>
// The compiler is satisfied. No error here.
// foo can only be one of the branches in the conditional type.
foo.fn(foo.arg);
Ответ №2:
Не совсем уверен в вашем варианте использования, но вышесказанное может быть достигнуто с помощью этого:
type ArgType = number | string
type Wrapper<T extends ArgType> = {
fn: (a: T) => void
arg: T
}
let foo!: Wrapper<number>
foo.fn(foo.arg)
let foo1!: Wrapper<string>
foo1.fn(foo1.arg)
Комментарии:
1. Спасибо за попытку. Мой пример был упрощен. Я ищу ответ на общий случай, когда мы не можем переместить объединение таким образом.