Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

背景

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

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

https://qiita.com/yumechi/items/9b4ce2d90165780d3243

しかし、このメソッドの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 の違いとか、ちょっと驚くこともあったが、実現できるということがわかったので学びがあってよかった。

yumechi
えんじにゃー。ぷりぷりかしこま。Pythonしかできない。
https://namonakimichi.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away