JavaScriptでは、データをグループ化してやり取りする基本的な方法はオブジェクトです。TypeScriptでは、それらをオブジェクト型で表現します。
見てきたように、それらは匿名にすることができます。
tsTryfunctiongreet (person : {name : string;age : number }) {return "Hello " +person .name ;}
または、インターフェースを使用するか
tsTryinterfacePerson {name : string;age : number;}functiongreet (person :Person ) {return "Hello " +person .name ;}
型エイリアスを使用して名前を付けることができます。
tsTrytypePerson = {name : string;age : number;};functiongreet (person :Person ) {return "Hello " +person .name ;}
上記の3つの例ではすべて、`name`プロパティ(`string`でなければならない)と`age`プロパティ(`number`でなければならない)を含むオブジェクトを受け取る関数を記述しました。
クイックリファレンス
重要な日常的な構文を一目で確認したい場合は、`type`と`interface`の両方のチートシートをご利用いただけます。
プロパティ修飾子
オブジェクト型の各プロパティは、型、プロパティがオプションかどうか、プロパティに書き込みができるかどうかを指定できます。
オプションのプロパティ
多くの場合、プロパティが設定されている可能性のあるオブジェクトを扱うことになります。そのような場合、名前の最後に疑問符(`?`)を追加することで、これらのプロパティをオプションとしてマークできます。
tsTryinterfacePaintOptions {shape :Shape ;xPos ?: number;yPos ?: number;}functionpaintShape (opts :PaintOptions ) {// ...}constshape =getShape ();paintShape ({shape });paintShape ({shape ,xPos : 100 });paintShape ({shape ,yPos : 100 });paintShape ({shape ,xPos : 100,yPos : 100 });
この例では、`xPos`と`yPos`の両方がオプションと見なされます。どちらか一方を提供するか、両方とも提供しないかを選択できます。そのため、上記の`paintShape`への呼び出しはすべて有効です。オプション性は、プロパティが設定されている場合は特定の型でなければならないことを意味するだけです。
これらのプロパティから読み取ることもできますが、`strictNullChecks`の下では、TypeScriptはそれらが潜在的に`undefined`であることを教えてくれます。
tsTryfunctionpaintShape (opts :PaintOptions ) {letxPos =opts .xPos ;letyPos =opts .yPos ;// ...}
JavaScriptでは、プロパティが設定されたことがなくても、アクセスできます。`undefined`という値が返されるだけです。`undefined`を特別な値として処理することで、それを処理できます。
tsTryfunctionpaintShape (opts :PaintOptions ) {letxPos =opts .xPos ===undefined ? 0 :opts .xPos ;letyPos =opts .yPos ===undefined ? 0 :opts .yPos ;// ...}
指定されていない値のデフォルト値を設定するこのパターンは非常に一般的であるため、JavaScriptにはそれをサポートする構文があります。
tsTryfunctionpaintShape ({shape ,xPos = 0,yPos = 0 }:PaintOptions ) {console .log ("x coordinate at",xPos );console .log ("y coordinate at",yPos );// ...}
ここでは、`paintShape`のパラメータにデストラクチャリングパターンを使用し、`xPos`と`yPos`にデフォルト値を指定しました。これで、`xPos`と`yPos`は`paintShape`の本体内では確実に存在しますが、`paintShape`を呼び出すユーザーにとってはオプションになります。
現在、デストラクチャリングパターン内に型注釈を配置する方法はありません。これは、次の構文がJavaScriptで既に異なる意味を持つためです。
tsTryfunctiondraw ({shape :Shape ,xPos :number = 100 /*...*/ }) {Cannot find name 'shape'. Did you mean 'Shape'?2552Cannot find name 'shape'. Did you mean 'Shape'?render (); shape Cannot find name 'xPos'.2304Cannot find name 'xPos'.render (); xPos }オブジェクトのデストラクチャリングパターンでは、`shape: Shape`は「`shape`プロパティを取得し、`Shape`という名前のローカル変数として再定義する」という意味です。同様に、`xPos: number`は、値がパラメータの`xPos`に基づく`number`という名前の変数を作成します。
readonly プロパティ
TypeScriptでは、プロパティをreadonlyとしてマークすることもできます。実行時の動作は変わりませんが、readonlyとしてマークされたプロパティは、型チェック中に書き込むことができません。
tsTryinterfaceSomeType {readonlyprop : string;}functiondoSomething (obj :SomeType ) {// We can read from 'obj.prop'.console .log (`prop has the value '${obj .prop }'.`);// But we can't re-assign it.Cannot assign to 'prop' because it is a read-only property.2540Cannot assign to 'prop' because it is a read-only property.obj .= "hello"; prop }
readonly修飾子を使用しても、値が完全に不変であること、つまり内部の内容を変更できないことが必ずしも意味するわけではありません。単に、プロパティ自体を書き換えることができないという意味です。
tsTryinterfaceHome {readonlyresident : {name : string;age : number };}functionvisitForBirthday (home :Home ) {// We can read and update properties from 'home.resident'.console .log (`Happy birthday ${home .resident .name }!`);home .resident .age ++;}functionevict (home :Home ) {// But we can't write to the 'resident' property itself on a 'Home'.Cannot assign to 'resident' because it is a read-only property.2540Cannot assign to 'resident' because it is a read-only property.home .= { resident name : "Victor the Evictor",age : 42,};}
readonlyが何を意味するのかという期待値を管理することが重要です。オブジェクトの使用方法に関する開発時の意図をTypeScriptに伝えるのに役立ちます。TypeScriptは、2つの型のプロパティがreadonlyかどうかを、それらの型が互換性があるかどうかをチェックする際に考慮しないため、readonlyプロパティはエイリアシングによって変更される可能性もあります。
tsTryinterfacePerson {name : string;age : number;}interfaceReadonlyPerson {readonlyname : string;readonlyage : number;}letwritablePerson :Person = {name : "Person McPersonface",age : 42,};// worksletreadonlyPerson :ReadonlyPerson =writablePerson ;console .log (readonlyPerson .age ); // prints '42'writablePerson .age ++;console .log (readonlyPerson .age ); // prints '43'
マッピング修飾子を使用すると、readonly属性を削除できます。
インデックスシグネチャ
型のプロパティの名前をすべて事前に知らない場合がありますが、値の形状はわかっています。
そのような場合は、インデックスシグネチャを使用して可能な値の型を記述できます。例:
tsTryinterfaceStringArray {[index : number]: string;}constmyArray :StringArray =getStringArray ();constsecondItem =myArray [1];
上記では、インデックスシグネチャを持つStringArrayインターフェースがあります。このインデックスシグネチャは、StringArrayがnumberでインデックス付けされると、stringを返すことを示しています。
インデックスシグネチャのプロパティには、string、number、symbol、テンプレート文字列パターン、およびこれらの型のみで構成されるユニオン型などの一部の型のみが許可されます。
両方のタイプのインデクサをサポートすることは可能です...
両方のタイプのインデクサをサポートすることは可能ですが、数値インデクサから返される型は、文字列インデクサから返される型のサブタイプである必要があります。これは、JavaScriptが数値でインデックス付けを行う場合、実際にはオブジェクトにインデックス付けする前にそれを文字列に変換するためです。つまり、100(number)でインデックス付けすることは、"100"(string)でインデックス付けすることと同じなので、両方が一致する必要があります。
tsTryinterfaceAnimal {name : string;}interfaceDog extendsAnimal {breed : string;}// Error: indexing with a numeric string might get you a completely separate type of Animal!interfaceNotOkay {['number' index type 'Animal' is not assignable to 'string' index type 'Dog'.2413'number' index type 'Animal' is not assignable to 'string' index type 'Dog'.x : number]:Animal ;[x : string]:Dog ;}
文字列インデックスシグネチャは「辞書」パターンを記述する強力な方法ですが、すべてのプロパティがその戻り値の型と一致することを強制します。これは、文字列インデックスがobj.propertyもobj["property"]として使用できることを宣言するためです。次の例では、nameの型は文字列インデックスの型と一致せず、型チェッカーはエラーを返します。
tsTryinterfaceNumberDictionary {[index : string]: number;length : number; // okProperty 'name' of type 'string' is not assignable to 'string' index type 'number'.2411Property 'name' of type 'string' is not assignable to 'string' index type 'number'.: string; name }
ただし、インデックスシグネチャがプロパティ型のユニオンである場合、異なる型のプロパティが許容されます。
tsTryinterfaceNumberOrStringDictionary {[index : string]: number | string;length : number; // ok, length is a numbername : string; // ok, name is a string}
最後に、インデックスシグネチャをreadonlyにすることで、そのインデックスへの代入を防ぐことができます。
tsTryinterfaceReadonlyStringArray {readonly [index : number]: string;}letmyArray :ReadonlyStringArray =getReadOnlyStringArray ();Index signature in type 'ReadonlyStringArray' only permits reading.2542Index signature in type 'ReadonlyStringArray' only permits reading.myArray [2] = "Mallory";
インデックスシグネチャがreadonlyなので、myArray[2]を設定することはできません。
超過プロパティチェック
オブジェクトに型が割り当てられる場所と方法は、型システムに影響を与える可能性があります。その重要な例の一つが超過プロパティチェックです。これは、オブジェクトが作成され、作成中にオブジェクト型に割り当てられるときに、オブジェクトをより徹底的に検証します。
tsTryinterfaceSquareConfig {color ?: string;width ?: number;}functioncreateSquare (config :SquareConfig ): {color : string;area : number } {return {color :config .color || "red",area :config .width ?config .width *config .width : 20,};}letArgument of type '{ colour: string; width: number; }' is not assignable to parameter of type 'SquareConfig'. Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write 'color'?2345Argument of type '{ colour: string; width: number; }' is not assignable to parameter of type 'SquareConfig'. Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write 'color'?mySquare =createSquare ({colour : "red",width : 100 });
createSquareに渡された引数は、colorではなくcolourとスペルミスされています。プレーンなJavaScriptでは、このようなことはサイレントに失敗します。
widthプロパティは互換性があり、colorプロパティが存在せず、余分なcolourプロパティは重要ではないため、このプログラムは正しく型付けされていると主張するかもしれません。
しかし、TypeScriptは、このコードにバグがある可能性が高いという立場を取っています。オブジェクトリテラルは特別な扱いを受け、他の変数に割り当てたり、引数として渡したりするときに、超過プロパティチェックが行われます。オブジェクトリテラルに「ターゲット型」にないプロパティがあると、エラーが発生します。
tsTryletArgument of type '{ colour: string; width: number; }' is not assignable to parameter of type 'SquareConfig'. Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write 'color'?2345Argument of type '{ colour: string; width: number; }' is not assignable to parameter of type 'SquareConfig'. Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write 'color'?mySquare =createSquare ({colour : "red",width : 100 });
これらのチェックを回避するのは実際には非常に簡単です。最も簡単な方法は、型アサーションを使用することです。
tsTryletmySquare =createSquare ({width : 100,opacity : 0.5 } asSquareConfig );
ただし、オブジェクトに特別な方法で使用される余分なプロパティがあることが確かな場合は、文字列インデックスシグネチャを追加する方が良いアプローチかもしれません。SquareConfigは、上記の型でcolorとwidthプロパティを持つことができますが、その他に任意の数のプロパティを持つこともできる場合、次のように定義できます。
tsTryinterfaceSquareConfig {color ?: string;width ?: number;[propName : string]: any;}
ここでは、SquareConfigは任意の数のプロパティを持つことができ、colorまたはwidthでない限り、その型は問題ないことを示しています。
これらのチェックを回避するもう1つの方法(少し驚くかもしれませんが)は、オブジェクトを別の変数に割り当てることです。squareOptionsの割り当ては超過プロパティチェックを受けないので、コンパイラはエラーを返しません。
tsTryletsquareOptions = {colour : "red",width : 100 };letmySquare =createSquare (squareOptions );
上記の回避策は、squareOptionsとSquareConfigに共通のプロパティがある限り機能します。この例では、widthプロパティでした。ただし、変数に共通のオブジェクトプロパティがない場合は失敗します。例:
tsTryletsquareOptions = {colour : "red" };letType '{ colour: string; }' has no properties in common with type 'SquareConfig'.2559Type '{ colour: string; }' has no properties in common with type 'SquareConfig'.mySquare =createSquare (); squareOptions
上記のような単純なコードの場合、これらのチェックを「回避」しようとするべきではないことに注意してください。メソッドを持ち、状態を保持するより複雑なオブジェクトリテラルの場合、これらのテクニックを念頭に置いておく必要があるかもしれませんが、大部分の超過プロパティエラーは実際にはバグです。
つまり、オプションバッグのようなものに対して超過プロパティチェックの問題が発生している場合は、いくつかの型宣言を修正する必要があるかもしれません。この場合、colorまたはcolourプロパティの両方を持つオブジェクトをcreateSquareに渡しても問題ない場合は、それを反映するようにSquareConfigの定義を修正する必要があります。
型の拡張
他の型のより具体的なバージョンである可能性のある型を持つことは非常に一般的です。たとえば、米国で手紙や小包を送信するために必要なフィールドを記述するBasicAddress型があるとします。
tsTryinterfaceBasicAddress {name ?: string;street : string;city : string;country : string;postalCode : string;}
状況によってはそれで十分ですが、住所には、住所にある建物に複数のユニットがある場合、ユニット番号が関連付けられていることがよくあります。次に、AddressWithUnitを記述できます。
tsTryinterfaceAddressWithUnit {name ?: string;unit : string;street : string;city : string;country : string;postalCode : string;}
これは機能しますが、変更が純粋に追加されただけなのに、BasicAddressの他のすべてのフィールドを繰り返す必要があったという欠点があります。代わりに、元のBasicAddress型を拡張し、AddressWithUnitに固有の新しいフィールドを追加できます。
tsTryinterfaceBasicAddress {name ?: string;street : string;city : string;country : string;postalCode : string;}interfaceAddressWithUnit extendsBasicAddress {unit : string;}
interfaceのextendsキーワードを使用すると、他の名前付き型のメンバーを効果的にコピーし、必要な新しいメンバーを追加できます。これは、記述する必要がある型宣言のボイラープレートの量を削減し、同じプロパティのいくつかの異なる宣言が関連している可能性があることを示すために役立ちます。たとえば、AddressWithUnitはstreetプロパティを繰り返す必要がなく、streetはBasicAddressに由来するため、読者はこれらの2つの型が何らかの方法で関連していることを知ることができます。
interfaceは複数の型から拡張することもできます。
tsTryinterfaceColorful {color : string;}interfaceCircle {radius : number;}interfaceColorfulCircle extendsColorful ,Circle {}constcc :ColorfulCircle = {color : "red",radius : 42,};
交差型
interfaceを使用すると、拡張することで他の型から新しい型を構築できました。TypeScriptは、主に既存のオブジェクト型を組み合わせるために使用される交差型と呼ばれる別の構成を提供します。
交差型は、&演算子を使用して定義されます。
tsTryinterfaceColorful {color : string;}interfaceCircle {radius : number;}typeColorfulCircle =Colorful &Circle ;
ここでは、ColorfulとCircleを交差させて、ColorfulとCircleのすべてのメンバーを持つ新しい型を作成しました。
tsTryfunctiondraw (circle :Colorful &Circle ) {console .log (`Color was ${circle .color }`);console .log (`Radius was ${circle .radius }`);}// okaydraw ({color : "blue",radius : 42 });// oopsArgument of type '{ color: string; raidus: number; }' is not assignable to parameter of type 'Colorful & Circle'. Object literal may only specify known properties, but 'raidus' does not exist in type 'Colorful & Circle'. Did you mean to write 'radius'?2345Argument of type '{ color: string; raidus: number; }' is not assignable to parameter of type 'Colorful & Circle'. Object literal may only specify known properties, but 'raidus' does not exist in type 'Colorful & Circle'. Did you mean to write 'radius'?draw ({color : "red",raidus : 42 });
インターフェースと交差型の比較
類似していますが、実際には微妙に異なる2つの型の組み合わせ方法を見てきました。インターフェースでは、extends句を使用して他の型から拡張することができ、交差型でも同様のことを行い、型エイリアスで結果に名前を付けることができました。両者の主な違いは、競合の処理方法であり、その違いは通常、インターフェースと交差型の型エイリアスのどちらを選択するかを決定する主な理由の1つです。
ジェネリックオブジェクト型
任意の値(string、number、Giraffeなど)を含むことができるBox型を考えてみましょう。
tsTryinterfaceBox {contents : any;}
現時点では、contentsプロパティはanyとして型付けされていますが、これは機能しますが、将来的に問題が発生する可能性があります。
代わりにunknownを使用することもできますが、そうすると、contentsの型が既にわかっている場合に、予防的なチェックを行う必要が生じたり、エラーが発生しやすい型アサーションを使用する必要が生じたりします。
tsTryinterfaceBox {contents : unknown;}letx :Box = {contents : "hello world",};// we could check 'x.contents'if (typeofx .contents === "string") {console .log (x .contents .toLowerCase ());}// or we could use a type assertionconsole .log ((x .contents as string).toLowerCase ());
型安全なアプローチの1つは、contentsの各型に対して異なるBox型を構築することです。
tsTryinterfaceNumberBox {contents : number;}interfaceStringBox {contents : string;}interfaceBooleanBox {contents : boolean;}
しかし、それはこれらの型を操作するために異なる関数、または関数のオーバーロードを作成する必要があることを意味します。
tsTryfunctionsetContents (box :StringBox ,newContents : string): void;functionsetContents (box :NumberBox ,newContents : number): void;functionsetContents (box :BooleanBox ,newContents : boolean): void;functionsetContents (box : {contents : any },newContents : any) {box .contents =newContents ;}
それは多くのボイラープレートコードになります。さらに、後で新しい型とオーバーロードを導入する必要があるかもしれません。これは、ボックス型とオーバーロードがすべて事実上同じであるため、イライラさせられます。
代わりに、型パラメータを宣言するジェネリックなBox型を作成できます。
tsTryinterfaceBox <Type > {contents :Type ;}
これは「Type型のBoxとは、contentsの型がTypeである何か」と読むことができます。後でBoxを参照する際には、Typeの代わりに型引数を指定する必要があります。
tsTryletbox :Box <string>;
Boxを実際の型のテンプレートと考えてください。Typeは、他の型に置き換えられるプレースホルダーです。TypeScriptがBox<string>を認識すると、Box<Type>内のTypeのすべてのインスタンスをstringに置き換え、{ contents: string }のようなものを使用して処理します。言い換えれば、Box<string>と以前のStringBoxは同じように動作します。
tsTryinterfaceBox <Type > {contents :Type ;}interfaceStringBox {contents : string;}letboxA :Box <string> = {contents : "hello" };boxA .contents ;letboxB :StringBox = {contents : "world" };boxB .contents ;
Boxは、Typeを何でも置き換えることができるため、再利用可能です。つまり、新しい型のボックスが必要な場合、新しいBox型を宣言する必要はありません(もちろん、必要であれば宣言することもできます)。
tsTryinterfaceBox <Type > {contents :Type ;}interfaceApple {// ....}// Same as '{ contents: Apple }'.typeAppleBox =Box <Apple >;
これにより、ジェネリック関数を使用することで、オーバーロードを完全に回避することもできます。
tsTryfunctionsetContents <Type >(box :Box <Type >,newContents :Type ) {box .contents =newContents ;}
型エイリアスもジェネリックになり得ることに注意する価値があります。新しいBox<Type>インターフェースを定義できましたが、
tsTryinterfaceBox <Type > {contents :Type ;}
代わりに型エイリアスを使用して定義しました。
tsTrytypeBox <Type > = {contents :Type ;};
型エイリアスは、インターフェースとは異なり、オブジェクト型以上のものを記述できるため、他の種類のジェネリックヘルパー型を作成するためにも使用できます。
tsTrytypeOrNull <Type > =Type | null;typeOneOrMany <Type > =Type |Type [];typeOneOrManyOrNull <Type > =OrNull <OneOrMany <Type >>;typeOneOrManyOrNullStrings =OneOrManyOrNull <string>;
型エイリアスについては、もう少し後で詳しく説明します。
Array型
ジェネリックオブジェクト型は、多くの場合、含まれる要素の型とは独立して動作する何らかのコンテナ型です。データ構造がこのように動作すると、異なるデータ型間で再利用できるため理想的です。
このハンドブック全体を通して、まさにそのような型を使用してきました。それはArray型です。number[]やstring[]のような型を記述するたびに、それは実際にはArray<number>とArray<string>の省略形です。
tsTryfunctiondoSomething (value :Array <string>) {// ...}letmyArray : string[] = ["hello", "world"];// either of these work!doSomething (myArray );doSomething (newArray ("hello", "world"));
上記のBox型と同様に、Array自体はジェネリック型です。
tsTryinterfaceArray <Type > {/*** Gets or sets the length of the array.*/length : number;/*** Removes the last element from an array and returns it.*/pop ():Type | undefined;/*** Appends new elements to an array, and returns the new length of the array.*/push (...items :Type []): number;// ...}
最新のJavaScriptでは、Map<K, V>、Set<T>、Promise<T>など、ジェネリックな他のデータ構造も提供されています。これは、Map、Set、Promiseの動作方法により、任意の型の集合で動作できることを意味します。
ReadonlyArray型
ReadonlyArrayは、変更してはならない配列を記述する特別な型です。
tsTryfunctiondoStuff (values :ReadonlyArray <string>) {// We can read from 'values'...constcopy =values .slice ();console .log (`The first value is ${values [0]}`);// ...but we can't mutate 'values'.Property 'push' does not exist on type 'readonly string[]'.2339Property 'push' does not exist on type 'readonly string[]'.values .("hello!"); push }
プロパティのreadonly修飾子と同様に、主に意図を伝えるためのツールです。ReadonlyArrayを返す関数を見ると、内容を変更してはならないことがわかります。ReadonlyArrayを受け取る関数を見ると、内容が変更される心配なく、任意の配列をその関数に渡すことができます。
Arrayとは異なり、使用できるReadonlyArrayコンストラクタはありません。
tsTrynew'ReadonlyArray' only refers to a type, but is being used as a value here.2693'ReadonlyArray' only refers to a type, but is being used as a value here.("red", "green", "blue"); ReadonlyArray
代わりに、通常のArrayをReadonlyArrayに代入できます。
tsTryconstroArray :ReadonlyArray <string> = ["red", "green", "blue"];
TypeScriptはArray<Type>に対してType[]という省略記法を提供するように、ReadonlyArray<Type>に対してreadonly Type[]という省略記法も提供しています。
tsTryfunctiondoStuff (values : readonly string[]) {// We can read from 'values'...constcopy =values .slice ();console .log (`The first value is ${values [0]}`);// ...but we can't mutate 'values'.Property 'push' does not exist on type 'readonly string[]'.2339Property 'push' does not exist on type 'readonly string[]'.values .("hello!"); push }
最後に注意すべき点として、readonlyプロパティ修飾子とは異なり、通常のArrayとReadonlyArrayの間では、代入可能性は双方向ではありません。
tsTryletx : readonly string[] = [];lety : string[] = [];x =y ;The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.4104The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.= y x ;
タプル型
タプル型は、含まれる要素の数と、特定の位置に含まれる型を正確に知っている別の種類のArray型です。
tsTrytypeStringNumberPair = [string, number];
ここで、StringNumberPairはstringとnumberのタプル型です。ReadonlyArrayと同様に、ランタイムでは表現されませんが、TypeScriptにとって重要です。型システムにとって、StringNumberPairは、インデックス0にstring、インデックス1にnumberを含む配列を表します。
tsTryfunctiondoSomething (pair : [string, number]) {consta =pair [0];constb =pair [1];// ...}doSomething (["hello", 42]);
要素数を超えるインデックスを使用しようとすると、エラーが発生します。
tsTryfunctiondoSomething (pair : [string, number]) {// ...constTuple type '[string, number]' of length '2' has no element at index '2'.2493Tuple type '[string, number]' of length '2' has no element at index '2'.c =pair [2 ];}
JavaScriptの配列デストラクチャリングを使用して、タプルをデストラクチャリングすることもできます。
tsTryfunctiondoSomething (stringHash : [string, number]) {const [inputString ,hash ] =stringHash ;console .log (inputString );console .log (hash );}
タプル型は、各要素の意味が「明らか」である、規約に依存したAPIで役立ちます。これにより、デストラクチャリングする際に変数名を自由に選択できます。上記の例では、要素
0と1に任意の名前を付けることができました。しかし、すべてのユーザーが同じ「明らか」な見解を持っているわけではないため、記述的なプロパティ名を持つオブジェクトを使用する方がAPIにとって適切かどうかを再考する価値があるかもしれません。
これらの長さチェック以外では、このような単純なタプル型は、特定のインデックスのプロパティを宣言し、数値リテラル型でlengthを宣言するArrayのバリエーションである型と同等です。
tsTryinterfaceStringNumberPair {// specialized propertieslength : 2;0: string;1: number;// Other 'Array<string | number>' members...slice (start ?: number,end ?: number):Array <string | number>;}
要素の型の後に疑問符(?)を付けることで、タプルはオプションのプロパティを持つことができることも興味深い点です。オプションのタプル要素は末尾にのみ配置でき、lengthの型にも影響します。
tsTrytypeEither2dOr3d = [number, number, number?];functionsetCoordinate (coord :Either2dOr3d ) {const [x ,y ,z ] =coord ;console .log (`Provided coordinates had ${coord .length } dimensions`);}
タプルにはrest要素を含めることもでき、rest要素は配列/タプル型でなければなりません。
tsTrytypeStringNumberBooleans = [string, number, ...boolean[]];typeStringBooleansNumber = [string, ...boolean[], number];typeBooleansStringNumber = [...boolean[], string, number];
StringNumberBooleansは、最初の2つの要素がそれぞれstringとnumberであるが、その後に任意の数のbooleanを持つ可能性のあるタプルを表します。StringBooleansNumberは、最初の要素がstringで、その後任意の数のbooleanがあり、最後にnumberで終わるタプルを表します。BooleansStringNumberは、先頭の要素が任意の数のbooleanで、最後にstring、numberが続くタプルを表します。
rest要素を持つタプルには設定された「長さ」がありません。異なる位置にある既知の要素の集合のみを持っています。
tsTryconsta :StringNumberBooleans = ["hello", 1];constb :StringNumberBooleans = ["beautiful", 2, true];constc :StringNumberBooleans = ["world", 3, true, false, true, false, true];
オプション要素とrest要素が役立つのはなぜでしょうか?それは、TypeScriptがタプルをパラメーターリストに対応させることができるためです。タプル型はrestパラメーターと引数で使用できるため、次のようになります。
tsTryfunctionreadButtonInput (...args : [string, number, ...boolean[]]) {const [name ,version , ...input ] =args ;// ...}
これは基本的に次と同等です。
tsTryfunctionreadButtonInput (name : string,version : number, ...input : boolean[]) {// ...}
これは、restパラメーターを使用して可変数の引数を受け取り、最小限の要素数が必要だが、中間変数を導入したくない場合に便利です。
readonlyタプル型
タプル型に関する最後の注意点として、タプル型にはreadonlyのバリアントがあり、配列の省略記法と同様に、前にreadonly修飾子を付けることで指定できます。
tsTryfunctiondoSomething (pair : readonly [string, number]) {// ...}
予想されるように、TypeScriptではreadonlyタプルのプロパティへの書き込みは許可されません。
tsTryfunctiondoSomething (pair : readonly [string, number]) {Cannot assign to '0' because it is a read-only property.2540Cannot assign to '0' because it is a read-only property.pair [0 ] = "hello!";}
タプルはほとんどのコードで作成されて変更されないままである傾向があるため、可能な限り型をreadonlyタプルとしてアノテーションすることは良いデフォルトです。これは、constアサーションを含む配列リテラルがreadonlyタプル型として推論されることを考えると、重要です。
tsTryletpoint = [3, 4] asconst ;functiondistanceFromOrigin ([x ,y ]: [number, number]) {returnMath .sqrt (x ** 2 +y ** 2);}Argument of type 'readonly [3, 4]' is not assignable to parameter of type '[number, number]'. The type 'readonly [3, 4]' is 'readonly' and cannot be assigned to the mutable type '[number, number]'.2345Argument of type 'readonly [3, 4]' is not assignable to parameter of type '[number, number]'. The type 'readonly [3, 4]' is 'readonly' and cannot be assigned to the mutable type '[number, number]'.distanceFromOrigin (); point
ここで、distanceFromOriginは要素を変更することはありませんが、変更可能なタプルを期待しています。pointの型はreadonly [3, 4]として推論されたため、その型はpointの要素が変更されないことを保証できないため、[number, number]と互換性がありません。