严格条件过滤类型(Typescript)

Strict conditional filtering type (Typescript)

我正在尝试创建严格的过滤界面:

type Filter<I, O extends I = I> = (value: I) => I extends O ? boolean : false

通过编写这一行,我想为一个函数定义一个类型约束:

  1. 接收类型 I
  2. 的值
  3. returns false 如果值 不是 预期类型(I extends O 不是 true
  4. returns true | false 如果值 是预期类型的​​ 并且匹配过滤器

但 Typescript 会忽略条件 return 类型:

type MessageA = { type: 'A' }
type MessageB = { type: 'B' }
type Message = MessageA | MessageB

const filter: Filter<Message, MessageA> = ({ type }) => type === 'A'
const inputMessage: Message = { type: 'B' }

if (filter(inputMessage)) {
  // the following line produces error
  const message: MessageA = inputMessage
  // because according to TS compiler
  // inputMessage is still `MessageA | MessageB`
}

逻辑上 filter(inputMessage) 如果 inputMessage 的类型为 MessageA,则可能会产生 true

我想了解"is it achievable?""how to write it properly?"如果是。


我不受打字稿版本的限制,目前安装了最新的(目前)打字稿3.9.5。我正在使用 VSCode 1.46,是否有任何区别。

解决方案 1

你可以试试这个:

type Filter<T, U extends T> = (candidate: T) => candidate is U;

const filter: Filter<Message, MessageA> = (message): message is MessageA => message.type === 'A'

但您仍然需要显式定义 return 类型 (: message is MessageA)。

解决方案 2

这个更复杂,但它使您的类型保护(改进)类型安全。

像这样为类型保护创建一个工厂:

namespace Refinement {
  class Hit<T> {
    constructor(readonly value: T) {}
  }

  class Miss {}

  type Result<T> = Hit<T> | Miss;

  export function hit<T> (value: T) {
    return new Hit(value);
  }

  export const miss = new Miss();

  export function create<T, U extends T>(refine: (candidate: T) => Result<U>): (candidate: T) => candidate is U {
    return (candidate): candidate is U => refine(candidate) instanceof Hit;
  }
}

用法:

declare const inputMessage: Message;

const filter = Refinement.create(
  (message: Message) => message.type === 'A'
    ? Refinement.hit(message)
    : Refinement.miss
)

if (filter(inputMessage)) {
  inputMessage; // MessageA
}

例如 fp-ts 使用此方法。