严格条件筛选类型(Typescript)

rdlzhqv9  于 2023-04-22  发布在  TypeScript
关注(0)|答案(2)|浏览(84)

我正在尝试创建严格的过滤接口:

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

通过写这一行,我想为一个函数定义一个类型约束:
1.接收I类型的值
1.如果值不是预期类型的,则返回falseI extends O不是true
1.如果value的期望类型并且匹配filter,则返回true | false
但是Typescript忽略条件返回类型:

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`
}

逻辑上,如果inputMessage的类型为MessageA,则filter(inputMessage)可能产生true
我想了解 “它是可以实现的吗?”“如何正确地写它?” 如果是。
我不受typescript版本的限制,目前安装的是最新的(目前)Typescript 3.9.5。我使用的是VSCode 1.46,是否有任何区别。

vddsk6oq

vddsk6oq1#

方案一

你可以试试这个:

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

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

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

方案二

这一个更复杂,但它使你的类型守卫(细化)类型安全。
为类型保护创建一个工厂,如下所示:

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就使用了这种方法。

cwtwac6a

cwtwac6a2#

通过使用Array.flatMap,您可以过滤值和类型,请参阅:

const values: Array<string | null> = ['asd', null, 'zxc', null]
const nonNullValues = values.flatMap(value => value ? [value] : []) // string[]

相关问题