オライリーの プログラミングTypeScript P.81に出てきたArray.from
の以下のような使い方を見て、最初意味が分からなかったため、調べてみました。
function fill(length: number, value: string): string[] {
return Array.from({length}, () => value)
}
fill(3, 'a')
// -> [ 'a', 'a', 'a' ]
Array.from について
Array.from
はMDNによると
Array.from() メソッドは、配列のようなオブジェクトや反復可能オブジェクトから、浅くコピーされた新しい Array インスタンスを生成します。
で、以下のサンプルのような使い方をします。
console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]
引数の確認をしてみると、第1引数は以下のようになっています。
arrayLike
配列に変換する配列のようなオブジェクトまたは反復可能オブジェクト
今回のケースを改めて見ても、{length}
が「配列のようなオブジェクト」にも「反復可能オブジェクト」にも見えなかったため、なぜこれで動くのかが分かりませんでした。
型定義を見てみる
Array.from
の型定義を見てみると、以下のように書かれていました。
interface ArrayConstructor {
/**
* Creates an array from an iterable object.
* @param iterable An iterable object to convert to an array.
*/
from<T>(iterable: Iterable<T> | ArrayLike<T>): T[];
/**
* Creates an array from an iterable object.
* @param iterable An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T, U>(iterable: Iterable<T> | ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[];
}
Iterable
ではなさそうなので、ArrayLike
の定義を見てみます。
interface ArrayLike<T> {
readonly length: number;
readonly [n: number]: T;
}
すると、上記のような2つのプロパティを持つオブジェクトをArrayLikeと判定しているようでした。
今回の例でいうと、以下のようにどちらの条件も満たしていることになり、{length}
はArrayLike<T>
にマッチします。
> const length = 3 // 仮で定義
// -> undefined
> {length}.length // `{length}` は `{length: length}`のようにプロパティ名と値の変数名が同じときに省略した記法
// -> 3
> {length}[0]
// -> undefined
もう少し書き方を変えてみると、以下のようなことをやっているイメージです。
> {hoge: 'hoge'}.hoge
// -> 'hoge'
> {0: 'fuga', 1: undefined}[0]
// -> 'fuga'
> {0: 'fuga', 1: undefined}[1]
// -> undefined
> {0: 'fuga', 1: undefined}[2]
// -> undefined
このようなことができるのは、JavaScript、TypeScriptが「対象物は何という名前のものなのか」というよりも、「どう振る舞うのか」、「何ができるのか」という構造を重視しているからです。