Как создать универсальные функции составления для дискриминируемых союзов - PullRequest
0 голосов
/ 25 сентября 2019

Я, вероятно, экспериментирую с ограничениями системы TypeScript здесь, но возможно ли вообще правильно определить makeIsBox для создания isBoxA путем компоновки более isA?

type A = 'A'
type B = 'B'
type Letter = A | B

const makeIsLetter = <L extends Letter>(checkLetter: L) => (letter: Letter): letter is L => letter === checkLetter

const isA = makeIsLetter('A')
const isB = makeIsLetter('B')

// Test
const takeA = (a: A) => void 0
const takeB = (b: B) => void 0

declare const a: Letter
declare const b: Letter

takeA(a) // should fail

if(isA(a)) {
  takeA(a) // should not fail
} else {
  takeA(a) // should fail
  takeB(a) // should not fail>
}

// So far so good.


type BoxA = { type: A, value: string }
type BoxB = { type: B, status: number }
type Box = BoxA | BoxB

const makeIsBox = <
  L extends Letter,
  Fn extends (letter: Letter) => letter is L
>( 
  fn: Fn
) => <B extends Box>(box: Box) => fn(box.type)

const isBoxA = makeIsBox(isA)

declare const box: Box

if (isBoxA(box)) {
  const value = box.value
}

TypeScript Playground

1 Ответ

1 голос
/ 25 сентября 2019

TypeScript не выведет для вас определенный тип защиты .Вы должны аннотировать его самостоятельно, например, так:

const makeIsBox = <L extends Letter>(fn: (letter: Letter) => letter is L) => (
  box: Box
): box is Extract<Box, { type: L }> => fn(box.type);

Это все еще должно быть общим в L.Он принимает функцию типа (l: Letter)=>l is L и возвращает функцию типа (b: Box)=>b is Extract<Box, {type: L}>.Это использует Extract тип утилиты , чтобы выбрать только члена объединения Box, который можно назначить на {type: L}.

Это должно работать так, как вы хотите:

const isBoxA = makeIsBox(isA);

declare const box: Box;

if (isBoxA(box)) {
  const value = box.value;
} else {
  const status = box.status;
}

Надеюсь, это поможет;удачи!

Ссылка на код

...