【小ネタ】TypeScript のラッピングの仕方にちょっと感動した

TypeScript 歴の浅い筆者が最近ちょっと感動した実装方法についての記事です。歴の深い方にとっては当たり前の可能性もありますこと、予めご了承ください。
2023.11.30

こんにちは、高崎@アノテーション です。

はじめに

長らく C/C++ 使いだった筆者ですが、最近は TypeScript にどっぷりでして、最近になって「こんな表現出来るんだ」と感動した内容について記事にします。

この記事の対象

私のように TypeScript 歴が浅い一年未満の方対象となります。

ラッピングについて

例えば C/C++ のメモリ破壊で煮え湯を飲まされたことがありますでしょうか。

私は結構あります。

その際mallocをラッピングして、破壊された形跡を残すための管理領域を要求サイズから追加して確保し、破壊が無いかをチェックしたりしていました。1

TypeScript の場合、例えばlibtestというライブラリにtestという、引数が number 型一つの string を返却する関数が用意されているとします。

この関数に対して、内々で少し処理を施し、

myTest.ts

import { test as testTest } from 'libtest';
    :
export const test = ( argTest: number ): string => {
    // 何かの処理をする
    return testTest(argTest);
}

こんな形の libtest にある test をラッピングすれば、他のソースからこのソースを import することで、さも libtest にある test を使いつつ必要な処理が施される、ということが可能になります。

このように同じ名前で簡単に定義出来るのはちょっと感動モノでした。2

さらに引数、戻り値にひとサジの工夫

上記のサンプルですと、argTest や戻り値は libtest を知っておかないといけませんが、例えば将来的に戻り値が変わった場合、関数定義も追随して変える必要があります。

そこで。

myTest.ts Vol.2

import { test as testTest } from 'libtest';
    :
export const test = ( argTest: Parameters<typeof testTest>[0] ): ReturnType<typeof testTest> => {
    // 何かの処理をする
    return testTest(argTest);
}

と実装すれば、libtest に追随することなく定義が可能です。

引数が増減すると追随する必要がありますが、それは呼び元からのそれこそ破壊的変更になりますのでご愛嬌。

おわりに

今回は TypeScript でちょっと感動したことを記事にしてみました。

皆様のご参考になれば幸いです。

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。
「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。
現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。
少しでもご興味あれば、アノテーション株式会社WEBサイト をご覧ください。


  1. 詳細はソースは割愛しますが、リアルタイムで破壊されたかどうかを検知する仕組みは当時遂に出来ませんでした。。 
  2. C/C++ でも GNU C であれば main が始まる前にmallocという名前の関数を定義して配置し、そこから glibc をロードして本 malloc の関数ポインタを取ってきて呼ばせれば malloc になりすませる、といった方法が出来なくはないですが…。