Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
根据TypeScript中区分的并集的参数自动推断返回类型_Typescript_Discriminated Union - Fatal编程技术网

根据TypeScript中区分的并集的参数自动推断返回类型

根据TypeScript中区分的并集的参数自动推断返回类型,typescript,discriminated-union,Typescript,Discriminated Union,我试图过滤数组并自动推断返回类型 enum Category { Fruit, Animal, Drink, } interface IApple { category: Category.Fruit taste: string } interface ICat { category: Category.Animal name: string } interface ICocktail { category: Category.Drink price:

我试图过滤数组并自动推断返回类型

enum Category {
  Fruit,
  Animal,
  Drink,
}

interface IApple {
  category: Category.Fruit
  taste: string
}

interface ICat {
  category: Category.Animal
  name: string
}

interface ICocktail {
  category: Category.Drink
  price: number
}

type IItem = IApple | ICat | ICocktail

const items: IItem[] = [
  { category: Category.Drink, price: 30 },
  { category: Category.Animal, name: 'Fluffy' },
  { category: Category.Fruit, taste: 'sour' },
]
现在我想过滤
,类似于:

// return type is IItem[], but I want it to be IFruit[]
items.filter(x => x.category === Category.Fruit)
我知道
Array#filter
太通用了,所以我尝试将其封装在自定义函数中:

const myFilter = (input, type) => {
  return input.filter(x => x.category === type)
}
所以,我所需要的就是添加类型,这很好。让我们试试:

第一个想法是添加返回条件类型:

const myFilter = <X extends IItem, T extends X['category']>(
  input: X[],
  type: T
): T extends Category.Fruit ? IApple[] : T extends Category.Drink ? ICocktail[] : ICat[] => {
  // TS error here
  return input.filter((x) => x.category === type)
}
但是R
扩展了什么?我不知道

第三个想法是使用重载,但是,这也不是一个好主意,因为它需要手动指定所有类型,就像idea#1一样


在现代TS中,是否可以仅使用编译器来解决此问题?

问题不在于
数组.prototype.filter()
,它实际上有一个调用签名,可用于根据回调缩小返回数组的类型:

interface Array<T> {
  filter<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S, 
    thisArg?: any
  ): S[];
}
这里,
isItemOfCategory
是一个通用函数,它接受类型为
v
的值
v
,可分配给
IItem['category']
(即
类别
枚举值之一)并返回一个回调函数,该函数接受一个
IItem
i
并返回一个
布尔值,编译器可以使用该值确定
i
是否为
提取
。。。它是“其
类别
属性为
V
类型的
项目
联合的成员”。让我们看看它的实际行动:

console.log(items.filter(isItemOfCategory(Category.Fruit)).map(x => x.taste)); // ["sour"]
console.log(items.filter(isItemOfCategory(Category.Drink)).map(x => x.price)); // [30]
console.log(items.filter(isItemOfCategory(Category.Animal)).map(x => x.name)); // ["Fluffy"]
看起来不错。我不认为有必要尝试进一步重构为
filter()
的不同类型的签名,因为现有的签名可以按照您想要的方式工作


问题不在于
Array.prototype.filter()
,它实际上有一个调用签名,可用于根据回调缩小返回数组的类型:

interface Array<T> {
  filter<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S, 
    thisArg?: any
  ): S[];
}
这里,
isItemOfCategory
是一个通用函数,它接受类型为
v
的值
v
,可分配给
IItem['category']
(即
类别
枚举值之一)并返回一个回调函数,该函数接受一个
IItem
i
并返回一个
布尔值,编译器可以使用该值确定
i
是否为
提取
。。。它是“其
类别
属性为
V
类型的
项目
联合的成员”。让我们看看它的实际行动:

console.log(items.filter(isItemOfCategory(Category.Fruit)).map(x => x.taste)); // ["sour"]
console.log(items.filter(isItemOfCategory(Category.Drink)).map(x => x.price)); // [30]
console.log(items.filter(isItemOfCategory(Category.Animal)).map(x => x.name)); // ["Fluffy"]
看起来不错。我不认为有必要尝试进一步重构为
filter()
的不同类型的签名,因为现有的签名可以按照您想要的方式工作


太棒了!我的实际问题是由多个嵌套平面图组成的,因此我必须进一步重构,但这是一个惊人的起点。非常感谢。太棒了!我的实际问题是由多个嵌套平面图组成的,因此我必须进一步重构,但这是一个惊人的起点。非常感谢。