如何为可区分的联合创建通用组合函数

How to create generic composing functions for discriminated unions

我可能正在尝试使用 TypeScript 系统的限制,但是是否可以通过组合 isA 来正确定义 makeIsBox 来创建 isBoxA

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

TypeScript 不会 infer a user-defined type guard 适合你。你必须自己注释它,像这样:

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 的函数和 returns 类型 (b: Box)=>b is Extract<Box, {type: L}> 的函数。那就是使用 Extract utility type 到 select 只是 Box 联盟的成员,它可以分配给 {type: L}

它应该按照你想要的方式工作:

const isBoxA = makeIsBox(isA);

declare const box: Box;

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

希望对您有所帮助;祝你好运!

Link to code