TypeScript,——strictNullChecks模式。

假设我有一个可空字符串数组(string | null)[]。什么是单表达式的方式,以这样的方式删除所有的空值,结果有类型字符串[]?

const array: (string | null)[] = ["foo", "bar", null, "zoo", null];
const filterdArray: string[] = ???;

数组中。过滤器在这里不起作用:

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

数组推导式可以工作,但TypeScript不支持。

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


当前回答

TypeScript有一些实用工具来推断数组的类型并排除null值:

const arrayWithNulls = ["foo", "bar", null, "zoo", null]

type ArrayWithoutNulls = NonNullable<typeof arrayWithNulls[number]>[]

const arrayWithoutNulls = arrayWithNulls.filter(x => x != null) as ArrayWithoutNulls

比在新数组上手动转换为string[]更长,但更安全。

循序渐进:

从原始数组中获取类型:

typeof arrayWithNulls[number] // => string | null

排除空值:

NonNullable<typeof arrayWithNulls[number]> // => string

让它成为一个数组:

NonNullable<typeof arrayWithNulls[number]>[] // => string[]

链接:

NonNullable(官方文档) typeof array[number](博客文章,我在官方文档中找不到任何关于它的东西)

其他回答

我认为这将是一个简单的方法,有更清晰的代码

const array: (string | null)[] = ['foo', 'bar', null, 'zoo', null];
const filteredArray: string[] = array.filter(a => !!a);

类似于@ bijouu -trouvaille的回答,你只需要声明<arg> is <Type>作为过滤器函数的输出:

array.filter((x): x is MyType => x !== null);

你可以将你的筛选结果转换为你想要的类型:

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

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

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[];

(操场上的代码)

简单地使用

array.filter(Boolean);

这对所有真值都适用。

遗憾的是,这里不提供类型推断,找到了这个解决方案 在这里


type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T; //from lodash 

function truthy<T>(value: T): value is Truthy<T> {
    return Boolean(value);  //  or !!value
}

const arr =["hello","felow","developer","",null,undefined];

const truthyArr = arr.filter(truthy);

// the type of truthyArr will be string[]

我已经多次回到这个问题上,希望一些新的Typescript特性或类型可以解决这个问题。

这里有一个简单的技巧,我很喜欢结合map和后续过滤器。

const animals = ['cat', 'dog', 'mouse', 'sheep'];

const notDogAnimals = animals.map(a => 
{
   if (a == 'dog')
   {
      return null!;   // just skip dog
   }
   else {
      return { animal: a };
   }
}).filter(a => a);

你会看到我返回null!它实际上变成了类型never——这意味着最终的类型没有null。

这与最初的问题略有不同,但我发现自己经常遇到这种情况,这有助于避免另一个方法调用。希望有一天Typescript会提出一个更好的方法。