How to type filtered list?

26 Views Asked by At
const list = ['rabbits', 'raccoons', 'reindeer', 'red pandas'] as const 
const filteredList = list.filter(e => e.includes('re'))

How to type filteredList? I think of (typeof list[number])[], and TS is OK with this, but since list[number] is just a single item, the type is more like an array with repetitive items to me, e.g. ['reindeer', 'reindeer'] or ['red pandas', 'red pandas']. I'm not sure if that is correct?

1

There are 1 best solutions below

0
Behemoth On BEST ANSWER

Actually TypeScript infers filteredList as type ("rabbits" | "raccoons" | "reindeer" | "red pandas")[] which is equal to (typeof list[number])[] and I think it's totally okay to go with that.

TypeScript Playground


If you wanted the most accurate type possible on filteredList you could use a custom helper type which filters typeof list at compile time. Then assert filteredList to that type. I came up with this inspired by this answer on Variadic Tuple Types by @jcalz.

Essentially, you check if an item in list is assignable to ${string}${U}${string} where U is your filter criteria (e. g. "re" ). If so, the item is kept in the resulting tuple type. If not, it is removed.

type FilterTuple<
  T extends readonly any[],
  U extends string,
> = T extends readonly [infer F, ...infer R]
  ? [F] extends [`${string}${U}${string}`]
    ? [F, ...FilterTuple<R, U>]
    : FilterTuple<R, U>
  : [];

const list = ["rabbits", "raccoons", "reindeer", "red pandas"] as const;
const filteredList = list.filter((e) => e.includes("re")) as FilterTuple<
  typeof list,
  "re"
>;
// const filteredList: ["reindeer", "red pandas"]

TypeScript Playground