严格条件过滤类型(Typescript)
Strict conditional filtering type (Typescript)
我正在尝试创建严格的过滤界面:
type Filter<I, O extends I = I> = (value: I) => I extends O ? boolean : false
通过编写这一行,我想为一个函数定义一个类型约束:
- 接收类型
I
的值
- returns
false
如果值 不是 预期类型(I extends O
不是 true
)
- 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
使用此方法。
我正在尝试创建严格的过滤界面:
type Filter<I, O extends I = I> = (value: I) => I extends O ? boolean : false
通过编写这一行,我想为一个函数定义一个类型约束:
- 接收类型
I
的值
- returns
false
如果值 不是 预期类型(I extends O
不是true
) - 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
使用此方法。