TypeScript过滤掉数组中的空值

Ser*_*eyS 46 typescript

TypeScript, - strictNullChecks模式.

假设我有一个可以为空的字符串数组(字符串| null)[].以表达式为string []的方式删除所有空值的单表达式方法是什么?

const array: (string | null)[] = ["foo", "bar", null, "zoo", null];
const filterdArray: string[] = ???;
Run Code Online (Sandbox Code Playgroud)

Array.filter在这里不起作用:

// Type '(string | null)[]' is not assignable to type 'string[]'
array.filter(x => x != null);
Run Code Online (Sandbox Code Playgroud)

数组理解可能有效,但TypeScript不支持它们.

实际上,通过从联合中删除具有一个特定类型的条目,可以将该问题推广到过滤任何联合类型的数组的问题.但是让我们关注具有null和未定义的联合,因为这些是最常见的用例.

Bij*_*lle 75

您可以在中使用类型谓词函数,.filter以避免选择退出严格类型检查:

function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
    return value !== null && value !== undefined;
}

const array: (string | null)[] = ['foo', 'bar', null, 'zoo', null];
const filteredArray: string[] = array.filter(notEmpty);
Run Code Online (Sandbox Code Playgroud)

或者你可以使用array.reduce<string[]>(...).

  • 这是Type Guards最有用的用法,也是最优雅的解决方案! (3认同)
  • 2022: ```function notNull&lt;T&gt;(value: T): value is NonNullable&lt;T&gt; { return value != null; }``` (3认同)

Mik*_*sky 64

还有一对,因为人们往往忽视了很好的措施flatMap,它可以处理filtermap一气呵成(这也并不需要任何铸造string[]):

// (string | null)[]
const arr = ["a", null, "b", "c"];
// string[]
const stringsOnly = arr.flatMap(f => f ? [f] : []);
Run Code Online (Sandbox Code Playgroud)

  • 为什么 Array.filter() 不这样做?TypeScript 对 Array.filter() 的支持不够好吗? (13认同)
  • 我相信这也会起作用 `arr.flatMap(f =&gt; f || [])` (9认同)
  • 很好的建议@Sarsaparilla,虽然我认为`??`会更好,因为`||`会过滤掉所有虚假值,比如`0`和`""` (8认同)
  • 这应该是最重要的答案。事实上,我什至会将其更改为 `f =&gt; !!f ?[f] : []` 进行简化。 (6认同)
  • 它可能会影响 [tsconfig.json](https://www.typescriptlang.org/tsconfig) 的 _compilerOptions_ 特别是“_lib_”部分 ([example](/sf/answers/3749163141/) ) (4认同)
  • 表演会是什么样的呢?尽管它看起来很“优雅”,但乍一看,它似乎会执行一堆不必要的操作。 (4认同)
  • 值得注意的是 [flatMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap) 是在 ECMA-262(又名 ECMAScript 2021)中定义的。对于某些人来说,这可能是一个障碍。 (3认同)
  • @AlexKlaus,这是一个 TypeScript 问题,ECMAScript 2021 与它有什么关系?您可以将 TypeScript 转译到许多目标。 (2认同)
  • 这会为“arr”中的每个元素分配一个新数组,这是不使用 IMO 的一个很好的理由。 (2认同)

std*_*453 44

刚刚意识到你可以这样做:

const nonNull = array.filter((e): e is Exclude<typeof e, null> => e !== null)
Run Code Online (Sandbox Code Playgroud)

这样你:

  1. 得到一个单行,没有附加功能
  2. 不必知道数组元素的类型,因此您可以在任何地方复制它!

  • 这应该是公认的想法,因为它使用实用程序类型(https://www.typescriptlang.org/docs/handbook/utility-types.html)向 TypeScript 提供类型缩小信息 (7认同)

DLi*_*ght 42

一班轮:

const filteredArray: string[] = array.filter((s): s is string => Boolean(s));
Run Code Online (Sandbox Code Playgroud)

TypeScript 游乐场

技巧是传递一个类型谓词:s is string语法)。

这个答案表明Array.filter需要用户提供类型谓词。

注意:如果您不想删除虚假值(例如空字符串),请替换Boolean(s)为。s !== null""

  • @AlexPo 不太清楚,所以我建议不要这样做 (9认同)
  • 可以使用 `!!s` (bang-bang) 代替 `Boolean(s)` (7认同)
  • 我真的很喜欢这个答案,我从未想过在匿名函数中使用类型保护。它还避免创建许多数组,[就像 Mike Sukmanowsky 的答案](/sf/answers/4180882191/)。 (2认同)

alu*_*ach 40

与@ bijou-trouvaille的答案类似,您只需将声明声明<arg> is <Type>为过滤函数的输出:

array.filter((x): x is MyType => x !== null);
Run Code Online (Sandbox Code Playgroud)

  • 吸引人的。但这不是类型安全的。这和使用“as”一样糟糕。如果你这样写:`const realArr: number[] = arr.filter((x): x is number =&gt; x === undefined);`,它实际上返回一个未定义的数组,那么 Typescript 不会抱怨。 (14认同)
  • @VivekMaharajh [用户定义的类型保护](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)从来都不像你看起来的那样“类型安全”预期: `const isString = (x: number | string): x is string =&gt; true;` 完全没问题,即使它会报告数字为 `true` 。如果你弄乱了你的类型保护,你的类型系统就会有问题。对于这个答案和被接受的答案来说同样如此。您实际上有一种“类型*更安全*”的方法来解决手头的问题吗? (4认同)
  • 我预计许多人会阅读本文,但没有意识到它包含未经检查的类型断言。这些人最终可能会复制/粘贴它,而不是编写不需要任何类型断言的更冗长的替代方案```` const removeNulls = (arr: (string | null)[]): string[] =&gt; { const输出:字符串[] = [];for (arr 的 const 值) { if (value !== null) { output.push(value); } 返回输出;}; ```` (4认同)
  • @VivekMaharajh 这是一个很好的观点,感谢您指出这一点。 (2认同)

Mat*_*nkt 14

这是一个使用的解决方案NonNullable。我发现它比 @bijou-trouvaille 接受的答案更简洁

function notEmpty<TValue>(value: TValue): value is NonNullable<TValue> {
    return value !== null && value !== undefined;
}
Run Code Online (Sandbox Code Playgroud)
const array: (string | null | undefined)[] = ['foo', 'bar', null, 'zoo', undefined];

const filteredArray: string[] = array.filter(notEmpty);
console.log(filteredArray)
[LOG]: ["foo", "bar", "zoo"]
Run Code Online (Sandbox Code Playgroud)


Iva*_*asa 12

您可以使用该is构造来缩小类型范围:

在此输入图像描述

在此输入图像描述

在此输入图像描述

在此输入图像描述


Nit*_*mer 11

您可以将filter结果转换为所需的类型:

const array: (string | null)[] = ["foo", "bar", null, "zoo", null];
const filterdArray = array.filter(x => x != null) as string[];
Run Code Online (Sandbox Code Playgroud)

这适用于您提到的更一般的用例,例如:

const array2: (string | number)[] = ["str1", 1, "str2", 2];
const onlyStrings = array2.filter(x => typeof x === "string") as string[];
const onlyNumbers = array2.filter(x => typeof x === "number") as number[];
Run Code Online (Sandbox Code Playgroud)

(游乐场代码)

  • 如果你有其他选择,最好避免使用“as” (2认同)

Rob*_*oli 11

为了避免每个人都不得不一遍又一遍地编写相同的类型保护辅助函数,我将名为 , 的函数捆绑isPresentisDefined一个isFilled辅助库中:https ://www.npmjs.com/package/ts-is-present

目前的类型定义是:

export declare function isPresent<T>(t: T | undefined | null): t is T;
export declare function isDefined<T>(t: T | undefined): t is T;
export declare function isFilled<T>(t: T | null): t is T;
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

export declare function isPresent<T>(t: T | undefined | null): t is T;
export declare function isDefined<T>(t: T | undefined): t is T;
export declare function isFilled<T>(t: T | null): t is T;
Run Code Online (Sandbox Code Playgroud)

当 Typescript 捆绑此功能时,我将删除该包。但是,现在,享受吧。


Dav*_*vid 8

如果您已经使用 Lodash,则可以使用compact. 或者,如果您更喜欢 Ramda,那么 ramda 辅助词也有compact功能。

两者都有类型,因此您的 tsc 会很高兴并获得正确的类型。

来自 Lodash d.ts 文件:

/**
 * Creates an array with all falsey values removed. The values false, null, 0, "", undefined, and NaN are
 * falsey.
 *
 * @param array The array to compact.
 * @return Returns the new array of filtered values.
 */
compact<T>(array: List<T | null | undefined | false | "" | 0> | null | undefined): T[];
Run Code Online (Sandbox Code Playgroud)