VS Code で税引前給与(総支給)を入力し、控除条件を反映した「手取り額」をその場で確認できる拡張機能は実装可能です。この記事では、相談先の選び方から、Command+InputBox の最小実装、WebView でのフォーム化、設定・テスト・配布までを実務目線で具体例つきで解説します。
VS Codeで「手取り計算(給与計算)」拡張機能は作れるのか
結論から言うと、VS Code の拡張機能(Extension)として「入力 → 計算 → 表示」を行うツールは問題なく作れます。VS Code 拡張は、エディタに新しい UI を追加したり、コマンドパレットから実行できる機能を増やしたり、WebView で簡易アプリのような画面を表示したりできます。
「手取り計算」のように、ユーザーが数値(総支給、控除、税率など)を入力し、結果(手取り、控除合計、内訳)を返すだけの用途なら、拡張機能の設計は比較的シンプルです。まずは“動くMVP(最小実装)”を作り、要件が固まったら UI と計算ロジックを強化するのが最短ルートです。
Microsoft Q&AでVS Codeの質問が進みにくい理由と、現実的な相談先
提示されたスレッドでは、Microsoft Q&A フォーラム側で「VS Code(Visual Studio Code)の話題はサポート対象外」という趣旨の案内が出ており、拡張機能の作り方そのものの技術回答は得られない流れでした。これは珍しい話ではなく、製品ごとにサポート範囲が明確に分かれているコミュニティでは起こりがちです。
代替の相談先として現実的なのは、VS Code 本体の公式 GitHub(microsoft/vscode)です。拡張機能の API 周り、WebView の制約、コマンドや設定のベストプラクティスなど、VS Code に直結する論点は GitHub 側の方が議論が成立しやすいです。
相談先を選ぶときの判断軸
- VS Code の仕組み・API・挙動が論点:公式 GitHub(vscode)や関連ドキュメント、既存 Issue/Discussion を優先
- 税計算ロジック(制度・計算式)が論点:国・地域の制度資料、税理士監修、社内の給与規程、あるいは計算ライブラリの検討
- 実装の一般的な質問(TypeScript/Node/HTML/CSS):Stack Overflow や開発者コミュニティが向く
特に給与・税に関しては、国・自治体・年度・扶養・控除・社会保険の区分などで計算が変わります。拡張機能側は「制度の正しさ」を自力で抱え込むと破綻しやすいので、まずは概算モデルや設定ベースで設計し、必要に応じて監修や仕様固めを進めるのが安全です。
どんなUIで作るのが現実的か(InputBoxかWebViewか)
「VS Code 内で手取り額を確認できる」要件は、UI の作り方によって体験が大きく変わります。最初から WebView に行くと早い反面、HTML/CSS/セキュリティ(CSP)など考慮点が増えます。迷ったら、まずは InputBox で MVP を作り、使われ方が見えたら WebView 化するのが堅実です。
| 方式 | 実装コスト | 使い勝手 | 向いている段階 | 主な表示先 |
|---|---|---|---|---|
| Command+InputBox | 低 | シンプルで速い | MVP/試作 | Notification / OutputChannel |
| Command+QuickPick | 低〜中 | 選択肢が多い条件に強い | MVP〜実用 | OutputChannel |
| WebView(フォーム画面) | 中〜高 | 入力・内訳表示が圧倒的に分かりやすい | 実用/配布 | WebView Panel |
| Status Bar 表示 | 中 | 常時見えるが情報量は少ない | 補助機能 | StatusBarItem |
「手取り」の定義を先に決める(ここを曖昧にすると必ず詰まる)
給与の「手取り」は、人によって“含めたい控除”や“知りたい単位”が異なります。拡張機能の完成度を上げるほど、仕様の曖昧さがバグやクレームに直結します。まずは次の2点を明確にするのが重要です。
- 対象の給与単位:月給ベース / 年収ベース / ボーナス込み など
- 控除の扱い:税(所得税・住民税)/社会保険/固定控除/任意控除(積立、組合費など)
制度に厳密に寄せるほど、入力項目が増えます。最初は「概算(簡易計算)」として割り切り、ユーザーが“自分の前提”を設定で調整できるようにする設計が実務的です。
| 項目 | 最低限MVP | 実用寄り | 厳密寄り |
|---|---|---|---|
| 総支給 | 必須 | 必須 | 必須(支給項目ごとの内訳も) |
| 控除率(概算) | 任意(デフォルト値) | 必須(設定で調整) | 不要(制度計算に置換) |
| 固定控除(例:定額の控除) | 任意 | 任意(複数項目) | 必須(項目多数) |
| 扶養/配偶者/控除条件 | 省略可 | 一部対応 | 必須 |
| 地域・年度 | 省略可 | 任意(テンプレ) | 必須 |
最小実装:Command+InputBoxで手取り概算を表示する
まずは「コマンドを実行 → 総支給を入力 → 控除率や控除額を入力 → 手取り概算を表示」という流れを作ります。ここまで動けば、“拡張として成立するか”の答えが出て、次の改善(WebView化や設定対応)にも進みやすくなります。
プロジェクト構成(例)
| パス | 役割 |
|---|---|
| src/extension.ts | コマンド登録、InputBox/表示、設定読み込み |
| src/salary/calc.ts | 手取り計算ロジック(UIと分離) |
| package.json | コマンド定義、設定スキーマ、表示名 |
package.json(コマンド定義の例)
拡張機能がコマンドパレットから呼び出せるようにします。
{
"activationEvents": [
"onCommand:tekitori.calc"
],
"contributes": {
"commands": [
{
"command": "tekitori.calc",
"title": "手取り計算(概算): 総支給から計算"
}
]
}
}
計算ロジック(概算モデルの例)
ここでは制度の厳密性よりも「拡張として動かす」ことを優先し、控除率+定額控除のモデルにします。後で差し替えられるよう、関数はUIから分離します。
// src/salary/calc.ts
export type Deduction = {
name: string;
kind: "rate" | "fixed";
value: number; // rate: 0.2 fixed: 10000
};
export type CalcInput = {
gross: number;
deductions: Deduction[];
};
export type CalcResult = {
gross: number;
totalDeduction: number;
net: number;
breakdown: { name: string; amount: number }[];
};
export function calcNet(input: CalcInput): CalcResult {
const gross = Math.max(0, Math.floor(input.gross));
const breakdown: { name: string; amount: number }[] = [];
for (const d of input.deductions) {
let amount = 0;
if (d.kind === "rate") {
amount = Math.round(gross * d.value);
} else {
amount = Math.round(d.value);
}
amount = Math.max(0, amount);
breakdown.push({ name: d.name, amount });
}
const totalDeduction = breakdown.reduce((sum, b) => sum + b.amount, 0);
const net = Math.max(0, gross - totalDeduction);
return { gross, totalDeduction, net, breakdown };
}
extension.ts(InputBoxで入力して結果を表示)
ユーザー入力は必ずバリデーションを入れます。コンマ区切り(例:350,000)にも対応すると実用性が上がります。
// src/extension.ts
import * as vscode from "vscode";
import { calcNet, Deduction } from "./salary/calc";
function parseMoney(input: string): number | null {
const normalized = input.replace(/,/g, "").trim();
if (normalized.length === 0) return null;
const n = Number(normalized);
if (!Number.isFinite(n)) return null;
return Math.floor(n);
}
function formatYen(n: number): string {
return `${n.toLocaleString("ja-JP")}円`;
}
export function activate(context: vscode.ExtensionContext) {
const output = vscode.window.createOutputChannel("手取り計算");
const disposable = vscode.commands.registerCommand("tekitori.calc", async () => {
const grossStr = await vscode.window.showInputBox({
title: "手取り計算(概算)",
prompt: "総支給(税引前)を入力してください(例:350000)",
placeHolder: "350000"
});
if (!grossStr) return;
const gross = parseMoney(grossStr);
if (gross === null || gross <= 0) {
vscode.window.showErrorMessage("総支給は正の数で入力してください。");
return;
}
const rateStr = await vscode.window.showInputBox({
title: "控除率(概算)",
prompt: "控除率(%)を入力してください(例:20)。空欄なら 20% を使用",
placeHolder: "20"
});
const ratePercent = rateStr ? Number(rateStr.trim()) : 20;
const rate = Number.isFinite(ratePercent) ? ratePercent / 100 : 0.2;
const fixedStr = await vscode.window.showInputBox({
title: "定額控除(任意)",
prompt: "定額控除を入力してください(例:10000)。空欄なら 0円",
placeHolder: "0"
});
const fixed = fixedStr ? (parseMoney(fixedStr) ?? 0) : 0;
const deductions: Deduction[] = [
{ name: "控除(概算・率)", kind: "rate", value: Math.max(0, Math.min(rate, 0.9)) },
{ name: "控除(定額)", kind: "fixed", value: Math.max(0, fixed) }
];
const result = calcNet({ gross, deductions });
output.clear();
output.appendLine(`総支給: ${formatYen(result.gross)}`);
output.appendLine(`控除合計: ${formatYen(result.totalDeduction)}`);
output.appendLine(`手取り概算: ${formatYen(result.net)}`);
output.appendLine("");
output.appendLine("内訳:");
for (const b of result.breakdown) {
output.appendLine(`- ${b.name}: ${formatYen(b.amount)}`);
}
output.show(true);
vscode.window.showInformationMessage(`手取り概算: ${formatYen(result.net)}(Outputに内訳を表示)`);
});
context.subscriptions.push(output, disposable);
}
export function deactivate() {}
ここまでで「VS Code 内で、総支給を入力して手取りを表示する」最小の要件は満たせます。次は、この機能を“毎回入力が面倒”にならない形へ改善していきます。
実用性を上げる:設定(settings.json)で控除条件を保存できるようにする
給与計算は、同じ人でも「概算の控除率」「毎月固定で引かれるもの」がだいたい決まっているケースが多いです。毎回 InputBox で入力させるより、拡張機能の設定で保存しておけるようにすると、一気に“使えるツール”になります。
設定項目の設計例(拡張設定として持つ)
| 設定キー例 | 型 | 意味 | 例 |
|---|---|---|---|
| tekitori.defaultDeductionRate | number | 概算控除率(0〜1) | 0.2 |
| tekitori.fixedDeductions | array | 定額控除の配列 | [{ “name”: “組合費”, “yen”: 1500 }] |
| tekitori.currency | string | 表示通貨(将来拡張用) | “JPY” |
package.json(設定スキーマの例)
{
"contributes": {
"configuration": {
"title": "手取り計算(給与計算)",
"properties": {
"tekitori.defaultDeductionRate": {
"type": "number",
"default": 0.2,
"minimum": 0,
"maximum": 0.9,
"description": "概算の控除率(0〜1)。例:0.2 は 20% を意味します。"
},
"tekitori.fixedDeductions": {
"type": "array",
"default": [],
"description": "定額控除の一覧(例:組合費、積立など)。",
"items": {
"type": "object",
"properties": {
"name": { "type": "string", "default": "定額控除" },
"yen": { "type": "number", "default": 0, "minimum": 0 }
},
"required": ["name", "yen"]
}
}
}
}
}
}
extension.ts側で設定を読む(例)
const config = vscode.workspace.getConfiguration();
const defaultRate = config.get<number>("tekitori.defaultDeductionRate", 0.2);
const fixedList = config.get<{ name: string; yen: number }[]>("tekitori.fixedDeductions", []);
const deductions: Deduction[] = [
{ name: "控除(概算・率)", kind: "rate", value: defaultRate },
...fixedList.map((d) => ({ name: d.name, kind: "fixed" as const, value: d.yen }))
];
こうすると、ユーザーは一度だけ設定を調整すれば、以降は総支給だけ入れて手取り概算を確認できるようになります。これは検索ニーズの高い「給与計算 簡易」「手取り ざっくり」「控除率 設定」などのキーワードにも相性が良い設計です。
WebViewで“アプリっぽい”給与計算フォームにする
InputBox は速い一方で、入力項目が増えるほど操作が煩雑になります。内訳や注意書き(概算であること、前提、保存の有無)を同じ画面で出したいなら WebView が強力です。WebView を使うと、VS Code 内に HTML フォームを表示し、ボタン押下で計算、結果をテーブルで表示…といった UX にできます。
WebView採用時に押さえるべきポイント
- セキュリティ:CSP(Content Security Policy)を設定し、インラインスクリプトを避ける
- データの受け渡し:WebView(ブラウザ側)と拡張(Node側)はメッセージ通信でやり取りする
- ロジック分離:計算は拡張側(TypeScript)に置き、WebView は UI に専念させると保守しやすい
メッセージ通信の流れ(概念)
| 方向 | 送信元 | 送信先 | 用途 | 例 |
|---|---|---|---|---|
| UI → 拡張 | WebView | Extension | 計算要求 | { type: “calc”, gross: 350000 } |
| 拡張 → UI | Extension | WebView | 計算結果 | { type: “result”, net: 280000, breakdown: […] } |
WebViewの実装イメージ(最小)
以下は「ボタンで計算 → 結果表示」の雰囲気を掴むための例です(実際はCSPやnonceなども含めて調整してください)。
// extension.ts(抜粋)
const panel = vscode.window.createWebviewPanel(
"tekitori",
"手取り計算(給与計算)",
vscode.ViewColumn.One,
{ enableScripts: true }
);
panel.webview.html = getWebviewHtml();
panel.webview.onDidReceiveMessage((msg) => {
if (msg.type === "calc") {
const gross = Number(msg.gross);
const result = calcNet({ gross, deductions });
panel.webview.postMessage({ type: "result", result });
}
});
<!-- WebView HTML(概念例) -->
<div>
<label>総支給(税引前)</label>
<input id="gross" type="text" placeholder="350000" />
<button id="btn">計算</button>
</div>
<div id="out"></div>
<script>
const vscode = acquireVsCodeApi();
document.getElementById("btn").addEventListener("click", () => {
const gross = document.getElementById("gross").value;
vscode.postMessage({ type: "calc", gross });
});
window.addEventListener("message", (event) => {
const msg = event.data;
if (msg.type === "result") {
const r = msg.result;
document.getElementById("out").innerText =
`手取り概算: ${r.net.toLocaleString()}円 / 控除合計: ${r.totalDeduction.toLocaleString()}円`;
}
});
</script>
WebView にすると、次のような“検索されやすい要件”にも対応しやすくなります。
- 複数の控除項目を一覧で追加・削除したい(例:定額控除を複数)
- 「手取り」「控除合計」「内訳」を表形式で同時に見たい
- 前回入力を保持して、入力の手間を減らしたい
給与計算を拡張機能に入れるときの“割り切り”と設計のコツ
給与の手取り計算を「正確に」やろうとすると、制度が絡んで複雑になりやすいです。拡張機能として価値を出すためには、次のような“割り切り”が現実的です。
割り切りの例
- 最初は概算として提供:控除率や定額控除はユーザー設定で調整可能にする
- 制度計算を目指すなら範囲を宣言:対象国・年度・前提(扶養の扱いなど)を明確化
- 内訳は“透明性”のために必ず出す:手取りだけだと不信感が出やすいので、控除の合計と項目を見せる
よくある落とし穴(給与計算あるある)
| 落とし穴 | 起こる問題 | 対策 |
|---|---|---|
| 「手取り」の定義が人によって違う | 期待値ズレ、クレーム | 前提を表示し、控除項目を明示する |
| 税・保険料は条件で変動 | 正確さを求めるほど入力項目が爆増 | 概算版と詳細版を分ける、設定で調整可能にする |
| 端数処理(四捨五入/切り捨て) | 数百円〜数千円の差が出る | 丸め規則を統一し、UIに明記する |
| 月給/年収の混同 | 大きな誤解を招く | 入力欄に単位を明記し、ラベルを分ける |
特に「端数処理」は軽視されがちですが、給与は“数字への敏感さ”が強い領域です。概算でも、どのタイミングで丸めるかを固定し、説明可能な状態にしておくとトラブルが減ります。
テストしやすい設計にする(UIと計算を分けるだけで勝てる)
拡張機能開発でありがちな失敗は、UI(InputBoxやWebView)と計算ロジックが絡み合ってしまい、変更のたびに壊れることです。計算は純粋関数(入力→出力)として切り出し、ユニットテストで担保するのが最もコスパが良いです。
最低限用意したいテストケース
| ケース | 入力 | 期待 | 狙い |
|---|---|---|---|
| 正常:控除率のみ | gross=300000, rate=0.2 | net=240000 | 基本式の確認 |
| 正常:定額控除追加 | gross=300000, rate=0.2, fixed=10000 | net=230000 | 内訳と合計の整合 |
| 境界:総支給が0以下 | gross=-1 | net=0 | 異常値対策 |
| 境界:控除が総支給を超える | gross=10000, fixed=999999 | net=0 | マイナス防止 |
| 入力:コンマ区切り | “350,000” | 350000として扱う | 実用性 |
テストを整えると、「計算式の変更」「控除項目の追加」「設定スキーマ変更」などの改修が怖くなくなります。給与系ツールは改修頻度が上がりやすいので、ここに投資する価値が高いです。
配布形態の選択:個人用か、チーム用か、公開か
VS Code 拡張は、作っただけでは終わりません。使う人が増えるほど、「どう配布するか」「どこまでサポートするか」が重要になります。
配布の選択肢
| 配布方法 | 向いている用途 | メリット | 注意点 |
|---|---|---|---|
| ローカルで実行(開発版) | 個人の検証 | 最速 | 他人に渡しにくい |
| VSIXで配布 | 社内配布・限定配布 | 公開不要で配れる | 更新配布の運用が必要 |
| Marketplaceで公開 | 一般公開 | 導入が簡単、発見されやすい | サポート・レビュー対応が発生 |
給与計算ツールで特に注意したい点
- 個人情報の扱い:氏名や社員番号などは極力扱わない。扱うなら保存先・暗号化・ログ出力を厳密に管理する
- 「概算」であることの明記:制度と一致しない可能性を UI と README に明確に記載する
- ロジック更新の方針:年度や制度変更がある場合、更新頻度・メンテ範囲を決めておく
VS Code公式GitHubで質問するときに通りやすい書き方
スレッドの案内どおり、VS Code 公式 GitHub 側で相談する場合は「再現可能」「論点がVS Code側にある」状態にして投稿するのが鍵です。給与計算ロジックそのものより、拡張機能 API の使い方・制約に焦点を当てると議論が成立しやすいです。
投稿に入れると良い情報
- やりたいこと:例「InputBoxで複数入力→WebViewで結果表、設定保存」
- 困っている点:例「WebViewのCSPでスクリプトが動かない」「onDidReceiveMessageが発火しない」
- 最小再現コード:コード断片ではなく、できれば小さなリポジトリか最小の手順
- 環境:OS、VS Code バージョン、拡張の言語(TS/JS)
また、既存の Issue/Discussion に同種の話題があることも多いので、検索してから投稿するとスムーズです。投稿の最初に「既存の関連スレッドを見たが、ここが違う」と書けると、読み手の負担が減ります。
“給与計算拡張”を一段上げる改善アイデア
MVPが動いたら、ユーザー体験と検索ニーズに効く改善を段階的に入れるのが効果的です。
改善アイデア(実務で効く順)
- 履歴:直近の入力(総支給)を保持し、再入力の手間を減らす
- 控除テンプレ:ユーザーが「自分用テンプレ」を作れる(例:A社用、B社用)
- 内訳の表表示:控除項目、控除額、控除率、手取りを1画面に
- 出力のコピー:結果をクリップボードにコピーできる(Slackやメモに貼れる)
- ステータスバー連携:直近結果を小さく常時表示(詳細はコマンドで開く)
内訳表の例(WebViewでもOutputでも見せる)
| 項目 | 種別 | 値 | 控除額(円) |
|---|---|---|---|
| 控除(概算・率) | 率 | 20% | 70,000 |
| 組合費 | 定額 | 1,500 | 1,500 |
| 積立 | 定額 | 5,000 | 5,000 |
「計算結果の透明性(内訳)」「設定で自分用に寄せられる」「入力の手間が少ない」の3点を押さえると、単なるサンプルを超えて“普段使いのツール”になります。
まとめ:最短で形にして、必要なところだけ厚くする
VS Code で手取り計算(給与計算)拡張機能を作ること自体は十分可能で、最初は Command+InputBox+OutputChannel の組み合わせで手早くMVPを作るのが現実的です。そこから、設定で控除条件を保存できるようにし、入力項目が増えるなら WebView でフォーム化すると、使い勝手と説得力が一気に上がります。
一方で、給与の「正確さ」を追いすぎると制度依存で複雑になりがちです。最初は概算モデル+内訳表示+設定で調整という形で価値を出し、制度対応が必要になった段階で仕様と責任範囲を明確にしてから進めるのが、長く使える拡張への近道です。

コメント