背景
ググラビティの問題からか、あまり類似した記事を見つけることができなかったので、記載してみようと思う。
以前こういう記事を書いた。
しかし、このメソッドの2引数のとり方は違和感がある。
function startsWith(target, pattern) {
return target.indexOf(pattern) === 0;
}
引き渡す順序を覚えていないといけないし、変数自身に聞けばいいような問題に思われる。可能ならこう呼びたいはず。
stringItem.startsWith(pattern);
もともとのプロジェクトを素のJavaScirptで書いていたのだが、TypeScript化するついでに、この問題を解決してみた。
調査: TypeString 自体に startsWith とかありそう?
なさそうだった。VSCodeで予測変換を噛ましてみたが、なさそうな感じだった。一応下記リンクとは一致していることを確認した。
TypeScript Strings - tutorialspoint
HandBookから探すのが難しい。どこを読むべきなんだろう。
Extension として書いたもの
参考にしたのは下記。
- TypeScript で動的にメソッドを追加する - ひびのログ
- 【TypeScript】拡張メソッドの実装(基本型) - 開発覚書はてな版
- How to extend String Prototype and use it next, in Typescript? - Stack Overflow
export {};
/**
* String Extender
*/
declare global {
interface String {
/**
* Return True if string starts with the prefix, otherwise return False.
* @param word
*/
startsWith(word: string): boolean;
/**
* Return True if string ends with the prefix, otherwise return False.
* @param word
*/
endsWith(word: string): boolean;
}
}
String.prototype['startsWith'] = function(word: string) {
return this.indexOf(word) === 0;
};
String.prototype['endsWith'] = function(word: string) {
return (
this.lastIndexOf(word) + word.length === this.length &&
word.length <= this.length
);
};
吐き出されたコードはこんな感じ。
// Compiled using ts2gas 1.6.2 (TypeScript 3.5.1)
var exports = exports || {};
var module = module || { exports: exports };
String.prototype['startsWith'] = function (word) {
return this.indexOf(word) === 0;
};
String.prototype['endsWith'] = function (word) {
return (this.lastIndexOf(word) + word.length === this.length &&
word.length <= this.length);
};
あ、メソッドの説明のところは Built-in Types — Python 3.7.3 documentation の startsWith メソッドの部分 を参考にしている。(私は Python の人であるため)
これを他のファイルでインポートしてやるといい感じに動いた。
ハマったこと
下記のように書くと、this
が捉えるスコープが広くなりすぎて( Logger を使って print してやると、なんかズラッとすべてのメソッド名なり、変数名なりが取れているので GAS 全体のオブジェクトが取れてそうな感じだった)思ったように動作しなかった。
調べてみるとアロー関数 - JavaScript | MDNがヒットしたので、読んでみると、「this を束縛しない」の章で納得。レキシカルスコープの this 値を使っているということで、理解した。
String.prototype.startsWith = (word: string): boolean => {
return this.indexOf(word) === 0;
};
吐き出されたコードがこれ。
// Compiled using ts2gas 1.6.2 (TypeScript 3.5.1)
var exports = exports || {};
var module = module || { exports: exports };
var _this = this;
String.prototype['startsWith'] = function (word) {
return _this.indexOf(word) === 0;
};
String.prototype['endsWith'] = function (word) {
return (_this.lastIndexOf(word) + word.length === _this.length &&
word.length <= _this.length);
};
実行してみると 実行に失敗: TypeError: オブジェクト [object Object] で関数 lastIndexOf が見つかりません。(行 9、ファイル「string.extensions」)(合計ランタイム 0.382 秒)
つまりそうなる。はい。
感想
できるだろう…! と思ったが、実現するまでに時間がかかりすぎてしまった。 prototype に突っ込むとか、アロー関数と function の違いとか、ちょっと驚くこともあったが、実現できるということがわかったので学びがあってよかった。