LoginSignup
2
2

More than 3 years have passed since last update.

Clasp+TypeScriptで、stringにメソッドを追加拡張してみる

Posted at

背景

ググラビティの問題からか、あまり類似した記事を見つけることができなかったので、記載してみようと思う。

以前こういう記事を書いた。

しかし、このメソッドの2引数のとり方は違和感がある。

function startsWith(target, pattern) {
  return target.indexOf(pattern) === 0; 
}

引き渡す順序を覚えていないといけないし、変数自身に聞けばいいような問題に思われる。可能ならこう呼びたいはず。

stringItem.startsWith(pattern);

もともとのプロジェクトを素のJavaScirptで書いていたのだが、TypeScript化するついでに、この問題を解決してみた。

調査: TypeString 自体に startsWith とかありそう?

なさそうだった。VSCodeで予測変換を噛ましてみたが、なさそうな感じだった。一応下記リンクとは一致していることを確認した。

TypeScript Strings - tutorialspoint

HandBookから探すのが難しい。どこを読むべきなんだろう。

microsoft/TypeScript-Handbook: The TypeScript Handbook is a comprehensive guide to the TypeScript language

Extension として書いたもの

参考にしたのは下記。

string.extension.ts
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
  );
};

吐き出されたコードはこんな感じ。

string.extension.gs
// 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.extension.bad.ts
String.prototype.startsWith = (word: string): boolean => {
  return this.indexOf(word) === 0;
};

吐き出されたコードがこれ。

string.extension.bad.gs
// 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 の違いとか、ちょっと驚くこともあったが、実現できるということがわかったので学びがあってよかった。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2