TypeScript: Array.prototype.mapでundefinedを取り除いた配列を作成する

こんにちは、いわむらです

今回はTypeScriptを使ってundefinedが含まれない配列を作成する方法をご紹介しようと思います

今回はtypescript v4.6.2で動作検証を進めるのでもし上手く動かない場合はバージョンを確認していただきたいです

undefinedが含まれない配列を作成したいにいたった理由なんですが、あるオブジェクトの特定プロパティのみの配列をループ処理を使って作成したい時に上手くいかずはまってました

サンプルのためにあるオブジェクトを名前プロパティが任意のユーザー型とします

type User = {
    /**
     * 
     * @type {number}
     * @memberof User
     */
    id: number;
    /**
     * 
     * @type {string}
     * @memberof User
     */
    name?: string;
}

ユーザーのリストを作成して名前を抽出した配列を作ろうと思います

まずはユーザーリストを作ります

const user1: User = {
    id: 1,
    name: 'test1',
} as User

const user2: User = {
    id: 2,
    name: 'test2',
}

const user3: User = {
    id: 3,
    name: 'test3',
}

const userList = [user1, user2, user3];

名前のみを抽出するメソッドを作成しようとしてArray.prototype.mapを使ってみるとundefinedが含まれるかもしれないからとエラーになります

const getUserNames = (users: User[]): string[] => {
    return users.map((u: User) => u.name)
}
// Type '(string | undefined)[]' is not assignable to type 'string[]'

そこでArray.prototype.filterでundefinedを取り除いてみます

Array.prototype.filterを利用したら上手くいくかと思ったのですがまた同じエラーでコンパイルが通りませんでした

const userNames = (userss: User[]): string[] => {
    return userss
            .map((u: User) => u.name)
            .filter(
                (name: string | undefined) => name !== undefined
            )
}

Array.prototype.filterの戻り値をユーザ定義型ガードでstringだとすることで解消します

nameにundefinedが含まれているのでundefinedを除いた型であると記述します

const getUserNames = (users: User[]): string[] => {
    return users
            .map((u: User) => u.name)
            .filter(
                (name): name is Exclude<typeof name, undefined> => {
                    return name !== undefined
                }
            )
}

これでコンパイルエラーがない状態で名前のみをもつ配列が作成できました

今回は文字列のみを対象としましたがidが任意プロパティであっても同じようにすることでnumber[]を返却するメソッドを作成できます

今回は以上になります。最後まで読んでいただきありがとうございました。