文字列はプリミティブ値なのにlengthプロパティをもつ?
length
プロパティ
文字列がもつ文字列に含まれる文字数を取得したいとき、length
プロパティにアクセスすることで取得することができます。
const str = "foobarbaz";
console.log(str.length); //9
文字列はプリミティブな値なので、プロパティを持たないはず。
しかしlength
プロパティを参照できているのは、一時的にオブジェクトが作成されて、そのオブジェクトが持つプロパティを参照しているためです。
文字列プリミティブに対してメソッドを呼び出したり、プロパティを参照したりするコンテキストでは、JavaScript は自動的に文字列プリミティブをラップし、ラッパーオブジェクトに対してメソッドを呼び出したり、プロパティを参照したりします。
length
プロパティと型
文字列がlength
というプロパティを実質的にもっているような記述となるため、TypeScript では{length: number}
など、Stringのプロパティやメソッドのみをもつ型のオブジェクトに文字列を代入してもコンパイルエラーが発生しないようになっています。
type T = { length: number; includes: (searchString: string) => boolean };
const obj: T = "foobarbaz"; //コンパイルエラーは発生しない
length
の型がStringオブジェクトの持つlength
の型と一致しない場合などにはコンパイルエラーを出してくれます。
type U = { length: string; includes: (searchString: number) => boolean };
const obj2: U = 'foobarbaz' //Type 'string' is not assignable to type 'U'.2322)
{}
型
文字列が{length: number}
型のオブジェクトに代入可能であることから、部分型である{}
型のオブジェクトにも代入可能です。
const obj:{} = 'foobarbaz' //コンパイルエラーは発生しない
{}
は、null
とundefined
以外の値は全て代入可能な型となっています。
const obj:{} = 234
const obj2:{} = false
const obj3:{} = Symbol
const obj4:{} = undefined //Type 'undefined' is not assignable to type '{}'.(2322)
const obj5:{} = null //Type 'null' is not assignable to type '{}'.(2322)
const obj6:{} = Math.random() > 0.5 ? 1 : undefined
//Type 'number | undefined' is not assignable to type '{}'.
//Type 'undefined' is not assignable to type '{}'.(2322)
サロゲートペア文字に注意
length
プロパティは文字数をあらわしているのではなく、コードユニットの数をあらわしています。
そのため、サロゲートペア文字(𩸽や𠮷)は長さ2としてカウントされてしまいます。(もっと大きな数値でカウントされる文字も存在します👨👩👧👧)
文字列を一度スプレッド構文で配列に入れてから配列の要素数を数えることで、見た目の文字数を数えることができます(スプレッド構文はコードポイントごとに文字列を分けてくれます)。
const str = '𩸽と𠮷'
console.log(str.length) //5
console.log([...str].length) //3
console.log([...str]) //["𩸽", "と", "𠮷"]
この方法でも絵文字などは直感に反する数値が返ってくるのでご注意を。
console.log([...'👨👩👧👧']) //["👨", "", "👩", "", "👧", "", "👧"]
console.log([...'👨👩👧👧'].length) //7
4人と数えられるわけでもないんですね。👨👩👧👧
Discussion