无法让嵌套类型保护与打字稿中的联合类型一起使用
Posted
技术标签:
【中文标题】无法让嵌套类型保护与打字稿中的联合类型一起使用【英文标题】:Unable to get nested type guards to work with union types in typescript 【发布时间】:2019-10-19 10:17:10 【问题描述】:我正在尝试通过删除任何虚假/错误值来清理数据,然后再将其传递给我的消费函数。但我无法让类型保护正常工作。
我遇到以下错误
类型“ExcludeUnusableValues”不可分配给类型“T”。
和
键入'错误 | prop1:字符串;道具2:字符串; ' 不可分配给类型 ' prop1: string;道具2:字符串; '。
interface BaseVariant
id: number
data: [s:string]:any | Error
interface VariationOne extends BaseVariant
type: 'One'
key1: string
data:
|
prop1: string
prop2: string
| Error
interface VariationTwo extends BaseVariant
type: 'Two'
key2: number
interface VariationThree extends BaseVariant
type: 'Three'
key3: number
data:
|
prop3: string
prop4: number
| Error
type Variations = VariationOne | VariationTwo | VariationThree
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
// Removes falsy/error values from object properties
type ExcludeUnusableValues<
T,
K extends keyof T,
C = Error | null
> = Omit<T, K> & [a in K]-?: Exclude<T[a], C>
const hasValidData = <T extends Variations>(item: T): item is ExcludeUnusableValues<T, 'data'> =>
return !(item.data instanceof Error)
const foo = (item: Variations) =>
if (!item || !hasValidData(item))
return null
switch (item.type)
case 'One':
return barOne(item);
case 'Two':
return barTwo(item);
case 'Three':
return barThree(item);
const barOne = (item: ExcludeUnusableValues<VariationOne, 'data'>) =>
console.log(item.data.prop1)
const barTwo = (item: ExcludeUnusableValues<VariationTwo, 'data'>) =>
console.log(item.data)
const barThree = (item: ExcludeUnusableValues<VariationThree, 'data'>) =>
console.log(item.data.prop3)
TypeScript PlayGround Link
【问题讨论】:
【参考方案1】:我将尝试通过首先清理 sn-p 来回答它,因为发生了很多事情:
// helper types
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
type Truthy<T, F> = [K in keyof T]-?: Exclude<T[K], F>
type PickTruthy<T, K extends keyof T, F = Error | null> = Pick<Truthy<T, F>, K>
// main code
interface VariationOne
type: "One"
data: foo: string | Error
interface VariationTwo
type: "Two"
data: baz: number | Error
type Variations = VariationOne | VariationTwo
declare function hasValidData<T extends Variations>(item: T): item is PickTruthy<T, "data">
现在我们在hasValidData()
(see in playground) 中看到了真正的错误:
类型谓词的类型必须可分配给其参数的类型。
类型
Pick<Truthy<T, Error>, "data">
不可分配给类型T
。
发生这种情况是因为类型谓词仅将一种类型细化(缩小)为另一种类型。如果结果类型比目标类型“宽”,它将不起作用。我们可以在unknown
(widest/Top type) 和never
(narrowest/Bottom type) (see in playground) 的帮助下确认这一点。
type Foo = number
declare function UnknownisFoo(x: unknown): x is Foo
declare function FooisNever(x: Foo): x is never
declare function NeverisFoo(x: never): x is Foo // ERROR!
declare function FooisUnknown(x: Foo): x is unknown // ERROR!
这意味着在我修改后的你的原始代码版本中:
-
类型
PickTruthy<T, "data">
也比Variations
窄:具有更多键的对象更“具体”
类型参数T extends Variations
比Variations
窄:T
是Variations
的SubType
由于这两种类型之间没有直接比较,因此您无法按照您描述的方式细化目标类型参数。
最好的办法是使用 isError()
谓词保护检查 item.data
本身是否错误
interface VariationOne
type: "One"
data: foo: string | Error
interface VariationTwo
type: "Two"
data: baz: number | Error
type Variations = VariationOne | VariationTwo
declare const x: Variations
const data = x
declare function isError(item: unknown): item is Error
if (isError(data))
console.log('ERROR:', data.message)
【讨论】:
以上是关于无法让嵌套类型保护与打字稿中的联合类型一起使用的主要内容,如果未能解决你的问题,请参考以下文章