Typescript 和 Array.filter() 一起工作

ext*_*axt 6 typescript

使用时Array.filter()我不确定如何实现我在下面描述的内容。

我不想为此创建一个新类型(但如果没有其他办法,那也没关系):

interface GPSLocation {
  lat: number
  lng: number
}

interface House {
  address: string
  location?: GPSLocation
}

const house01: House = {
  address: '123 street'
}

const house02: House = {
  address: '123 street',
  location: {
    lat: 111.111,
    lng: 222.222
  }
}

const allHouses = [house01, house02]

// Infered by Typescript: const gpsLocationList: (GPSLocation | undefined)[]
// Expected: const gpsLocationList: (GPSLocation)[]
const gpsLocationList = allHouses.filter((house) => house.location !== undefined).map(house => house.location)
Run Code Online (Sandbox Code Playgroud)

Jua*_*des 8

让您的过滤器回调成为强制位置不可选的类型保护。

我知道有一个现有的帖子有一个很好的答案,但由于推断这种情况的解决方案(映射到可选子属性)并不容易,并且我在过去几天不得不做同样的事情,我将分享这个这里。

const gpsLocationList = allHouses
    .filter((house): house is House & {location: GPSLocation} => {
         return house.location !== undefined;
    })
    .map(house => house.location);
Run Code Online (Sandbox Code Playgroud)

为了清楚起见,我宁愿将其分成一个单独的声明。

type HouseLocationRequired = House & {location: GPSLocation};

const gpsLocationList = allHouses
    .filter((house): house is HouseLocationRequired => {
         return house.location !== undefined;
    })
    .map(house => house.location);
Run Code Online (Sandbox Code Playgroud)

如果你想真正减少类型的重复,你可以使用Required<T>Pick<>

type HouseLocationRequired = House & Required<Pick<House, "location">>;
Run Code Online (Sandbox Code Playgroud)

并将其进一步抽象一层:

type RequiredSubProperty<Class, Prop extends keyof Class> = Class & Required<Pick<Class, Prop>>;

const gpsLocationList = allHouses
    .filter((house): house is RequiredSubProperty<House, "location">  => {
         return house.location !== undefined;
    })
    .map(house => house.location);
Run Code Online (Sandbox Code Playgroud)

此时,您还可以使用类型安全来抽象检查子属性。

function subPropFilter<T>(prop: keyof T) {
    return (obj: T): obj is RequiredSubProperty<T, typeof prop> => {
        return obj[prop] !== undefined;
    }
}

const gpsLocationList = allHouses.
    .filter(subPropFilter("location"))
    .map(house => house.location)

Run Code Online (Sandbox Code Playgroud)

那么为什么不抽象整个过程,将映射添加到该子属性呢?

function getNonNullSubProps<T, Prop extends keyof T>(arr: T[], prop: Prop) {
    return arr.filter(subPropFilter(prop)).map(obj => obj[prop] as typeof obj[Prop]) 
}

const gpsLocationList = getNonNullSubProps(allHouses, "location");

Run Code Online (Sandbox Code Playgroud)

请参阅实时示例,该示例显示了当您使用这些实用程序时这些建议是多么棒。