有没有办法从函数内部的类型保护中保持联合类型的类型推断?
Is there a way to keep type inference in union types from type guards inside a function?
假设我有三种类型(和一种并集)
type A = {
type: 'a'
title: string
description: string
}
type B = {
type: 'b'
title: string
}
type C = {
type: 'c'
description: string
}
type D = A | B | C
我知道我可以使用 === 运算符进行正确的类型推断
function logger(t: D) {
if (t.type === 'a' || t.type === 'b') console.log(t.title) // no problems here
if (t.type === 'a' || t.type === 'c') console.log(t.description) // no problems here
}
但是,是否可以编写一个效用函数:
function matches<T extends { type: string }>(t: T, types: T['type'][]) : boolean {
return types.includes(t.type)
}
所以我可以毫无错误地做到这一点?
function logger(t: D) {
if (matches(t, ['a', 'b'])) console.log(t.title) // Property 'title' does not exist on type D
if (matches(t, ['a', 'c'])) console.log(t.description) // Property 'description' does not exist on type D
}
您可以将 matches()
写成 user-defined type guard function,以便编译器理解您对函数的 true
/false
输出的意图,以缩小t
参数。
这是一种方法:
function matches<T extends { type: string }, K extends string>(
t: T, types: K[]
): t is Extract<T, { type: K }> {
const widenedTypes: readonly string[] = types;
return widenedTypes.includes(t.type);
}
这是 generic both in T
, the type of t
, but also K
, the union of string literal types of the elements of types
. The return type is Extract<T, {type: K}>
, which uses the Extract<T, U>
utility type 以将联合类型 T
过滤为仅可分配给 {type: K}
的那些成分。
注意编译器会报错types.includes(t.type)
,因为types
的元素比t.type
窄。我处理这个问题的方法是首先(安全地)将 types
从 K[]
扩大到 readonly string[]
,然后在其上调用 includes()
。有关详细信息,请参阅 及其答案。
让我们看看它是否有效:
function logger(t: D) {
if (matches(t, ['a', 'b'])) console.log(t.title) // okay
if (matches(t, ['a', 'c'])) console.log(t.description) // okay
}
看起来不错!
假设我有三种类型(和一种并集)
type A = {
type: 'a'
title: string
description: string
}
type B = {
type: 'b'
title: string
}
type C = {
type: 'c'
description: string
}
type D = A | B | C
我知道我可以使用 === 运算符进行正确的类型推断
function logger(t: D) {
if (t.type === 'a' || t.type === 'b') console.log(t.title) // no problems here
if (t.type === 'a' || t.type === 'c') console.log(t.description) // no problems here
}
但是,是否可以编写一个效用函数:
function matches<T extends { type: string }>(t: T, types: T['type'][]) : boolean {
return types.includes(t.type)
}
所以我可以毫无错误地做到这一点?
function logger(t: D) {
if (matches(t, ['a', 'b'])) console.log(t.title) // Property 'title' does not exist on type D
if (matches(t, ['a', 'c'])) console.log(t.description) // Property 'description' does not exist on type D
}
您可以将 matches()
写成 user-defined type guard function,以便编译器理解您对函数的 true
/false
输出的意图,以缩小t
参数。
这是一种方法:
function matches<T extends { type: string }, K extends string>(
t: T, types: K[]
): t is Extract<T, { type: K }> {
const widenedTypes: readonly string[] = types;
return widenedTypes.includes(t.type);
}
这是 generic both in T
, the type of t
, but also K
, the union of string literal types of the elements of types
. The return type is Extract<T, {type: K}>
, which uses the Extract<T, U>
utility type 以将联合类型 T
过滤为仅可分配给 {type: K}
的那些成分。
注意编译器会报错types.includes(t.type)
,因为types
的元素比t.type
窄。我处理这个问题的方法是首先(安全地)将 types
从 K[]
扩大到 readonly string[]
,然后在其上调用 includes()
。有关详细信息,请参阅
让我们看看它是否有效:
function logger(t: D) {
if (matches(t, ['a', 'b'])) console.log(t.title) // okay
if (matches(t, ['a', 'c'])) console.log(t.description) // okay
}
看起来不错!