変数宣言

letconstは、JavaScriptにおける変数宣言の比較的新しい概念です。前述したようにletはいくつかの点でvarに似ていますが、JavaScriptでユーザーが遭遇する一般的な「落とし穴」を回避できます。

constは、変数への再代入を防ぐという点でletの拡張です。

TypeScriptはJavaScriptの拡張であるため、この言語は自然にletconstをサポートしています。ここでは、これらの新しい宣言と、それらがvarよりも好ましい理由について詳しく説明します。

JavaScriptを簡単に使用したことがある場合は、次のセクションで復習すると良いでしょう。JavaScriptのvar宣言のすべての癖に精通している場合は、先に進む方が簡単かもしれません。

var宣言

JavaScriptでの変数の宣言は、従来からvarキーワードで行われてきました。

ts
var a = 10;

お分かりのように、aという名前の変数を値10で宣言しました。

関数内で変数を宣言することもできます。

ts
function f() {
var message = "Hello, world!";
return message;
}

そして、他の関数内から同じ変数にアクセスすることもできます。

ts
function f() {
var a = 10;
return function g() {
var b = a + 1;
return b;
};
}
var g = f();
g(); // returns '11'

上記の例では、gfで宣言された変数aを取得しました。gが呼び出された時点では、aの値はf内のaの値に結び付けられます。fの実行が終了した後でも、gaにアクセスして変更できます。

ts
function f() {
var a = 1;
a = 2;
var b = g();
a = 3;
return b;
function g() {
return a;
}
}
f(); // returns '2'

スコープルール

var宣言には、他の言語に慣れている人にとっては奇妙なスコープルールがあります。次の例を見てください。

ts
function f(shouldInitialize: boolean) {
if (shouldInitialize) {
var x = 10;
}
return x;
}
f(true); // returns '10'
f(false); // returns 'undefined'

この例に驚いた読者もいるかもしれません。変数xifブロック内で宣言されていますが、そのブロックの外からアクセスできました。これは、var宣言は、後で説明する関数、モジュール、名前空間、グローバルスコープ内のどこにでもアクセスできるためです。これはvarスコープまたは関数スコープと呼ばれることがあります。パラメーターも関数スコープです。

これらのスコープルールは、いくつかの種類のミスを引き起こす可能性があります。それらが悪化する問題の1つは、同じ変数を複数回宣言してもエラーにならないという事実です。

ts
function sumMatrix(matrix: number[][]) {
var sum = 0;
for (var i = 0; i < matrix.length; i++) {
var currentRow = matrix[i];
for (var i = 0; i < currentRow.length; i++) {
sum += currentRow[i];
}
}
return sum;
}

経験豊富なJavaScript開発者にとってはすぐに気付く点かもしれませんが、内部のforループは、iが同じ関数スコープの変数を参照しているため、変数iを誤って上書きしてしまいます。経験豊富な開発者ならご存じのとおり、このようなバグはコードレビューをすり抜け、無限のフラストレーションの源になる可能性があります。

変数キャプチャの特異性

次のスニペットの出力がどうなるか、少し考えてみてください。

ts
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 100 * i);
}

ご存知ない方のために説明すると、setTimeoutは、(他の処理が停止するのを待つものの)一定のミリ秒後に関数の実行を試みます。

準備はよろしいでしょうか?見てみましょう。

10 10 10 10 10 10 10 10 10 10

多くのJavaScript開発者はこの動作に精通していますが、もし驚いたとしても、決してあなただけではありません。ほとんどの人は、出力が以下のようになると予想します。

0 1 2 3 4 5 6 7 8 9

前に述べた変数キャプチャについて覚えていますか?setTimeoutに渡す各関数式は、実際には同じスコープの同じiを参照しています。

それが何を意味するのか、少し考えてみましょう。setTimeoutは、一定のミリ秒後に関数を呼び出しますが、それはあくまでforループの実行が停止した後です。forループの実行が停止するまでに、iの値は10になっています。そのため、指定された関数が呼び出されるたびに、10が出力されるのです!

一般的な回避策は、IIFE(すぐに実行される関数式)を使用して、各反復でiをキャプチャすることです。

ts
for (var i = 0; i < 10; i++) {
// capture the current state of 'i'
// by invoking a function with its current value
(function (i) {
setTimeout(function () {
console.log(i);
}, 100 * i);
})(i);
}

この一見奇妙なパターンは、実際にはかなり一般的です。パラメータリストのiは、forループで宣言されたiを実際にシャドウしますが、同じ名前を付けたため、ループ本体を大きく変更する必要はありませんでした。

let宣言

ここまでで、varにはいくつかの問題があることがお分かりいただけたと思いますが、まさにこれがlet文が導入された理由です。使用されるキーワードを除けば、let文はvar文と同じように記述されます。

ts
let hello = "Hello!";

重要な違いは構文ではなく、セマンティクスにあります。これから詳しく見ていきましょう。

ブロックスコープ

letを使用して変数を宣言すると、レキシカルスコープまたはブロックスコープと呼ばれるものを使用します。スコープが包含する関数にリークするvarで宣言された変数とは異なり、ブロックスコープ変数は、最も近い包含ブロックまたはforループの外からは見えません。

ts
function f(input: boolean) {
let a = 100;
if (input) {
// Still okay to reference 'a'
let b = a + 1;
return b;
}
// Error: 'b' doesn't exist here
return b;
}

ここでは、ローカル変数abが2つあります。aのスコープはfの本体に限定され、bのスコープは包含するif文のブロックに限定されます。

catch節で宣言された変数にも、同様のスコープルールが適用されます。

ts
try {
throw "oh no!";
} catch (e) {
console.log("Oh well.");
}
// Error: 'e' doesn't exist here
console.log(e);

ブロックスコープ変数のもう1つの特性は、実際に宣言される前に読み書きできないことです。これらの変数はスコープ全体に「存在」しますが、宣言までのすべてのポイントは、その一時的デッドゾーンの一部です。これは、let文の前にアクセスできないという、洗練された言い方です。幸いなことに、TypeScriptはそれを教えてくれます。

ts
a++; // illegal to use 'a' before it's declared;
let a;

宣言前にブロックスコープ変数をキャプチャすることはできます。ただし、宣言前にその関数を呼び出すことはできません。ES2015をターゲットにしている場合、最新のランタイムはエラーをスローします。ただし、現時点ではTypeScriptは許容的で、これをエラーとして報告しません。

ts
function foo() {
// okay to capture 'a'
return a;
}
// illegal call 'foo' before 'a' is declared
// runtimes should throw an error here
foo();
let a;

一時的デッドゾーンの詳細については、Mozilla Developer Networkの関連コンテンツを参照してください。

再宣言とシャドウイング

var宣言では、変数を何回宣言しても、1つしか得られないことを説明しました。

ts
function f(x) {
var x;
var x;
if (true) {
var x;
}
}

上記の例では、xのすべての宣言は実際には同じxを参照しており、これは完全に有効です。これはしばしばバグの原因になります。ありがたいことに、let宣言はそれほど寛容ではありません。

ts
let x = 10;
let x = 20; // error: can't re-declare 'x' in the same scope

TypeScriptが問題であることを教えてくれるために、変数が両方ともブロックスコープである必要はありません。

ts
function f(x) {
let x = 100; // error: interferes with parameter declaration
}
function g() {
let x = 100;
var x = 100; // error: can't have both declarations of 'x'
}

ブロックスコープ変数を関数スコープ変数で宣言できないという意味ではありません。ブロックスコープ変数は、明確に異なるブロック内で宣言する必要があります。

ts
function f(condition, x) {
if (condition) {
let x = 100;
return x;
}
return x;
}
f(false, 0); // returns '0'
f(true, 0); // returns '100'

よりネストされたスコープに新しい名前を導入することをシャドウイングといいます。これは、偶発的なシャドウイングによって特定のバグを独自に導入する一方、特定のバグを防ぐことができるため、両刃の剣です。例えば、以前のsumMatrix関数をlet変数を使用して記述していたとします。

ts
function sumMatrix(matrix: number[][]) {
let sum = 0;
for (let i = 0; i < matrix.length; i++) {
var currentRow = matrix[i];
for (let i = 0; i < currentRow.length; i++) {
sum += currentRow[i];
}
}
return sum;
}

このループバージョンは、内部ループのiが外部ループのiをシャドウするため、実際に合計を正しく実行します。

シャドウイングは、より明確なコードを作成するために、通常は避けるべきです。それを利用するのが適切なシナリオもありますが、最善の判断をしてください。

ブロックスコープ変数のキャプチャ

var宣言による変数キャプチャのアイデアに触れたとき、キャプチャされた変数の挙動について簡単に説明しました。これについてより直感的に理解するために、スコープが実行されるたびに、変数の「環境」が作成されます。その環境とそのキャプチャされた変数は、そのスコープ内のすべての処理が終了した後も存在できます。

ts
function theCityThatAlwaysSleeps() {
let getCity;
if (true) {
let city = "Seattle";
getCity = function () {
return city;
};
}
return getCity();
}

その環境内からcityをキャプチャしたため、ifブロックの実行が終了した後でも、それにアクセスできます。

以前のsetTimeoutの例では、forループの各反復で変数の状態をキャプチャするためにIIFEを使用する必要がありました。実際に行っていたのは、キャプチャされた変数に対して新しい変数環境を作成することでした。これは少し面倒でしたが、幸いなことに、TypeScriptではもう二度と行う必要はありません。

let宣言は、ループの一部として宣言された場合、まったく異なる動作をします。ループ自体に新しい環境を導入するのではなく、これらの宣言は各反復ごとに新しいスコープを作成します。これはIIFEで行っていたことと同じなので、古いsetTimeoutの例をlet宣言を使用するように変更できます。

ts
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 100 * i);
}

そして、予想通り、これは以下を出力します。

0 1 2 3 4 5 6 7 8 9

const宣言

const宣言は、変数を宣言するもう1つの方法です。

ts
const numLivesForCat = 9;

let宣言に似ていますが、名前が示すように、バインドされると値を変更できません。つまり、letと同じスコープルールを持ちますが、再代入することはできません。

これは、参照する値が不変であるという意味と混同しないでください。

ts
const numLivesForCat = 9;
const kitty = {
name: "Aurora",
numLives: numLivesForCat,
};
// Error
kitty = {
name: "Danielle",
numLives: numLivesForCat,
};
// all "okay"
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;

特別な対策を取らない限り、const変数の内部状態は変更可能です。幸いなことに、TypeScriptではオブジェクトのメンバーをreadonlyとして指定できます。「インターフェースに関する章」に詳細があります。

letconst

同様のスコープセマンティクスを持つ2種類の宣言があるため、どちらを使用すべきかを尋ねるのは自然です。ほとんどの広範な質問と同様に、答えは状況によります。

最小権限の原則を適用すると、変更する予定のない宣言はすべてconstを使用する必要があります。その理由は、変数を書き換える必要がない場合、同じコードベースで作業している他の開発者がオブジェクトに書き込むことができず、変数に再代入する必要があるかどうかを検討する必要があるためです。constを使用すると、データの流れを推論する際のコードの予測可能性も向上します。

最善の判断を行い、該当する場合はチームの他のメンバーと相談してください。

このハンドブックの大部分は、let宣言を使用しています。

デストラクチャリング

TypeScriptが備えるECMAScript 2015のもう一つの機能として、デストラクチャリングがあります。詳細については、Mozilla Developer Networkの記事を参照してください。このセクションでは、簡単な概要を説明します。

配列デストラクチャリング

デストラクチャリングの最も単純な形式は、配列デストラクチャリング代入です。

ts
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2

これにより、firstsecondという2つの新しい変数が作成されます。これはインデックスを使用することと同等ですが、はるかに便利です。

ts
first = input[0];
second = input[1];

デストラクチャリングは、既に宣言されている変数でも機能します。

ts
// swap variables
[first, second] = [second, first];

そして、関数の引数でも機能します。

ts
function f([first, second]: [number, number]) {
console.log(first);
console.log(second);
}
f([1, 2]);

リスト内の残りのアイテムを変数に格納するには、...構文を使用します。

ts
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]

もちろん、これはJavaScriptなので、不要な末尾の要素を無視することもできます。

ts
let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1

他の要素も同様です。

ts
let [, second, , fourth] = [1, 2, 3, 4];
console.log(second); // outputs 2
console.log(fourth); // outputs 4

タプルデストラクチャリング

タプルは配列のようにデストラクチャリングできます。デストラクチャリング変数は、対応するタプルの要素の型を取得します。

ts
let tuple: [number, string, boolean] = [7, "hello", true];
let [a, b, c] = tuple; // a: number, b: string, c: boolean

タプルの要素の範囲を超えてデストラクチャリングしようとすると、エラーになります。

ts
let [a, b, c, d] = tuple; // Error, no element at index 3

配列と同様に、...を使用してタプルの残りの部分をデストラクチャリングし、より短いタプルを取得できます。

ts
let [a, ...bc] = tuple; // bc: [string, boolean]
let [a, b, c, ...d] = tuple; // d: [], the empty tuple

または、末尾の要素、またはその他の要素を無視できます。

ts
let [a] = tuple; // a: number
let [, b] = tuple; // b: string

オブジェクトデストラクチャリング

オブジェクトもデストラクチャリングできます。

ts
let o = {
a: "foo",
b: 12,
c: "bar",
};
let { a, b } = o;

これにより、o.ao.bから新しい変数abが作成されます。cは必要ない場合はスキップできることに注意してください。

配列デストラクチャリングと同様に、宣言なしで代入することもできます。

ts
({ a, b } = { a: "baz", b: 101 });

この文を括弧で囲む必要があったことに注意してください。JavaScriptは通常、{をブロックの開始として解釈します。

オブジェクト内の残りのアイテムを変数に格納するには、...構文を使用します。

ts
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;

プロパティの名前変更

プロパティに異なる名前を付けることもできます。

ts
let { a: newName1, b: newName2 } = o;

ここで構文が分かりにくくなります。a: newName1は「anewName1として」と解釈できます。方向は左から右で、次のように記述した場合と同じです。

ts
let newName1 = o.a;
let newName2 = o.b;

紛らわしいことに、ここでのコロンは型を示すものではありません。型を指定する場合は、デストラクチャリング全体の後にも記述する必要があります。

ts
let { a: newName1, b: newName2 }: { a: string; b: number } = o;

デフォルト値

デフォルト値を使用すると、プロパティが未定義の場合にデフォルト値を指定できます。

ts
function keepWholeObject(wholeObject: { a: string; b?: number }) {
let { a, b = 1001 } = wholeObject;
}

この例では、b?bがオプションであることを示しているので、undefinedになる可能性があります。keepWholeObjectには、bが未定義であっても、wholeObjectとプロパティabの変数が含まれるようになりました。

関数宣言

デストラクチャリングは、関数宣言でも機能します。単純なケースでは、これは簡単です。

ts
type C = { a: string; b?: number };
function f({ a, b }: C): void {
// ...
}

しかし、引数にはデフォルト値を指定することが一般的であり、デストラクチャリングでデフォルト値を正しく設定するのは難しい場合があります。まず、パターンをデフォルト値の前に置く必要があることを覚えておく必要があります。

ts
function f({ a = "", b = 0 } = {}): void {
// ...
}
f();

上記のコードスニペットは、ハンドブックで前述した型推論の例です。

次に、メインイニシャライザではなく、デストラクチャリングされたプロパティでオプションのプロパティのデフォルト値を指定することを覚えておく必要があります。Cbをオプションとして定義されていたことを思い出してください。

ts
function f({ a, b = 0 } = { a: "" }): void {
// ...
}
f({ a: "yes" }); // ok, default b = 0
f(); // ok, default to { a: "" }, which then defaults b = 0
f({}); // error, 'a' is required if you supply an argument

デストラクチャリングは慎重に使用してください。前の例で示したように、最も単純なデストラクチャリング式以外は何でも分かりにくくなります。これは、名前変更、デフォルト値、型注釈を積み重ねることなく、深くネストされたデストラクチャリングでは特に理解するのが難しくなります。デストラクチャリング式を小さくシンプルに保つようにしてください。デストラクチャリングによって生成される代入式を自分で記述することもできます。

スプレッド構文

スプレッド演算子は、デストラクチャリングとは逆のものです。配列を別の配列に、またはオブジェクトを別のオブジェクトに展開できます。例えば

ts
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];

これにより、bothPlusには[0, 1, 2, 3, 4, 5]という値が設定されます。スプレッド構文はfirstsecondの浅いコピーを作成します。スプレッド構文によって変更されることはありません。

オブジェクトも展開できます。

ts
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };

ここで、search{ food: "rich", price: "$$", ambiance: "noisy" }となります。オブジェクトのスプレッド構文は、配列のスプレッド構文よりも複雑です。配列のスプレッド構文と同様に、左から右に処理されますが、結果はオブジェクトのままです。つまり、スプレッドオブジェクトの後にあるプロパティは、前のプロパティを上書きします。そのため、前の例を最後にスプレッドするように変更すると

ts
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { food: "rich", ...defaults };

defaultsfoodプロパティがfood: "rich"を上書きします。これはこのケースでは望ましい結果ではありません。

オブジェクトのスプレッド構文にも、他にもいくつかの驚くべき制限があります。まず、オブジェクト自身の列挙可能なプロパティのみが含まれます。基本的に、オブジェクトのインスタンスを展開すると、メソッドが失われます。

ts
class C {
p = 12;
m() {}
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!

第二に、TypeScriptコンパイラは、ジェネリック関数からの型パラメータのスプレッドを許可しません。この機能は、将来の言語バージョンで期待されています。

using宣言

using宣言は、Stage 3 Explicit Resource Management提案の一部である、JavaScriptの今後の機能です。using宣言はconst宣言と非常によく似ていますが、宣言にバインドされた値のライフタイムと変数のスコープを結び付ける点が異なります。

using宣言を含むブロックから制御が抜けたとき、宣言された値の[Symbol.dispose]()メソッドが実行され、その値によるクリーンアップが可能になります。

ts
function f() {
using x = new C();
doSomethingWith(x);
} // `x[Symbol.dispose]()` is called

実行時には、これはおおよそ次のものと同等の効果があります。

ts
function f() {
const x = new C();
try {
doSomethingWith(x);
}
finally {
x[Symbol.dispose]();
}
}

using宣言は、ファイルハンドルなどのネイティブ参照を保持するJavaScriptオブジェクトを操作する場合に、メモリリークを回避するのに非常に役立ちます。

ts
{
using file = await openFile();
file.write(text);
doSomethingThatMayThrow();
} // `file` is disposed, even if an error is thrown

または、トレースなどのスコープ付き操作でも役立ちます。

ts
function f() {
using activity = new TraceActivity("f"); // traces entry into function
// ...
} // traces exit of function

varletconstとは異なり、using宣言はデストラクチャリングをサポートしていません。

nullundefined

値がnullまたはundefinedになる可能性があることに注意することが重要です。その場合、ブロックの終わりで何も破棄されません。

ts
{
using x = b ? new C() : null;
// ...
}

これは、おおよそ次のものと同等です。

ts
{
const x = b ? new C() : null;
try {
// ...
}
finally {
x?.[Symbol.dispose]();
}
}

これにより、複雑な分岐や繰り返しを行うことなく、using宣言を宣言するときに、条件付きでリソースを取得できます。

破棄可能なリソースの定義

生成するクラスまたはオブジェクトが破棄可能であることを示すには、Disposableインターフェースを実装します。

ts
// from the default lib:
interface Disposable {
[Symbol.dispose](): void;
}
// usage:
class TraceActivity implements Disposable {
readonly name: string;
constructor(name: string) {
this.name = name;
console.log(`Entering: ${name}`);
}
[Symbol.dispose](): void {
console.log(`Exiting: ${name}`);
}
}
function f() {
using _activity = new TraceActivity("f");
console.log("Hello world!");
}
f();
// prints:
// Entering: f
// Hello world!
// Exiting: f

await using宣言

非同期で実行する必要があるクリーンアップ処理が必要となるリソースや操作があります。これに対応するため、「明示的なリソース管理」提案ではawait using宣言も導入されています。明示的なリソース管理

ts
async function f() {
await using x = new C();
} // `await x[Symbol.asyncDispose]()` is invoked

await using宣言は、その値の`[Symbol.asyncDispose]()`メソッドを呼び出し、**await**します。これは、データベーストランザクションでのロールバックまたはコミット、またはストレージへの保留中の書き込みをフラッシュするファイルストリームなど、非同期のクリーンアップを可能にします。

awaitと同様に、await usingは、非同期関数またはメソッド内、またはモジュールの最上位レベルでのみ使用できます。

非同期的に破棄可能なリソースの定義

usingDisposableなオブジェクトに依存するように、await usingAsyncDisposableなオブジェクトに依存します。

ts
// from the default lib:
interface AsyncDisposable {
[Symbol.asyncDispose]: PromiseLike<void>;
}
// usage:
class DatabaseTransaction implements AsyncDisposable {
public success = false;
private db: Database | undefined;
private constructor(db: Database) {
this.db = db;
}
static async create(db: Database) {
await db.execAsync("BEGIN TRANSACTION");
return new DatabaseTransaction(db);
}
async [Symbol.asyncDispose]() {
if (this.db) {
const db = this.db:
this.db = undefined;
if (this.success) {
await db.execAsync("COMMIT TRANSACTION");
}
else {
await db.execAsync("ROLLBACK TRANSACTION");
}
}
}
}
async function transfer(db: Database, account1: Account, account2: Account, amount: number) {
using tx = await DatabaseTransaction.create(db);
if (await debitAccount(db, account1, amount)) {
await creditAccount(db, account2, amount);
}
// if an exception is thrown before this line, the transaction will roll back
tx.success = true;
// now the transaction will commit
}

await usingawait

await using宣言の一部であるawaitキーワードは、リソースの破棄がawaitされることを示すだけです。値自体をawaitするわけではありません。

ts
{
await using x = getResourceSynchronously();
} // performs `await x[Symbol.asyncDispose]()`
{
await using y = await getResourceAsynchronously();
} // performs `await y[Symbol.asyncDispose]()`

await usingreturn

Promiseを最初にawaitせずにPromiseを返す非同期関数でawait using宣言を使用する場合、この動作には小さな注意点があることに注意することが重要です。

ts
function g() {
return Promise.reject("error!");
}
async function f() {
await using x = new C();
return g(); // missing an `await`
}

返されたPromiseがawaitされていないため、`x`の非同期破棄を`await`している間に実行が一時停止し、返されたPromiseを購読していないため、JavaScriptランタイムが未処理の拒否を報告する可能性があります。ただし、これはawait using特有の問題ではなく、try..finallyを使用する非同期関数でも発生する可能性があります。

ts
async function f() {
try {
return g(); // also reports an unhandled rejection
}
finally {
await somethingElse();
}
}

この状況を回避するには、返された値がPromiseである可能性がある場合は、その返された値をawaitすることをお勧めします。

ts
async function f() {
await using x = new C();
return await g();
}

forおよびfor...of文でのusingawait using

usingawait usingの両方をfor文で使用できます。

ts
for (using x = getReader(); !x.eof; x.next()) {
// ...
}

この場合、`x`のライフタイムはfor文全体にスコープされ、breakreturnthrowによって制御がループから抜けるか、ループ条件が偽になった場合にのみ破棄されます。

for文に加えて、両方の宣言をfor...of文でも使用できます。

ts
function * g() {
yield createResource1();
yield createResource2();
}
for (using x of g()) {
// ...
}

ここでは、`x`はループの各反復の最後に破棄され、次に次の値で再初期化されます。これは、ジェネレータによって1つずつ生成されるリソースを使用する場合に特に役立ちます。

古いランタイムでのusingawait using

Symbol.dispose/Symbol.asyncDisposeの互換性のあるポリフィル(最近のNodeJSのバージョンでデフォルトで提供されているものなど)を使用している限り、古いECMAScriptエディションをターゲットにしている場合でも、usingawait using宣言を使用できます。

TypeScriptドキュメントはオープンソースプロジェクトです。プルリクエストを送信して、これらのページの改善にご協力ください by sending a Pull Request

このページへの貢献者
DRDaniel Rosenwasser (58)
OTOrta Therox (20)
NSNathan Shively-Sanders (9)
VRVimal Raghubir (3)
BCBrett Cannon (3)
24+

最終更新日: 2024年3月21日