TypeScript の型演算子
TypeScript は、強力な型計算機能を提供し、さまざまな型演算子を使用して既存の型を計算したり、新しい型を取得したりできます。
キーオブ演算子
導入
keyof は、オブジェクト型をパラメータとして受け取り、オブジェクトのすべてのキー名で構成される共用体型を返す単項演算子です。
タイプ MyObj = {
foo: 数値、
バー: 文字列、
};
type Keys = keyof MyObj // 'foo'|'bar';
上記の例では、keyof MyObj
は、MyObj
のすべてのキー名で構成される共用体型、つまり 'foo'|'bar'
を返します。
別の例を示します。
インターフェース T {
0: ブール値。
a:文字列。
b(): 無効;
}
タイプ KeyT = keyof T;
JavaScript オブジェクトのキー名は 3 種類しかないため、どのオブジェクトのキー名の共用体型も string|number|symbol
になります。
// 文字列数値記号 |
KeyT = 任意のキーを入力します。
カスタム キー名のない型の場合は、keyof 演算子を使用して「never」型を返し、そのような型のキー名を持つことが不可能であることを示します。
type KeyT = keyof object; // 決して
上の例では、object
型には独自のプロパティがないため、キー名がないため、keyof object
は never
型を返します。
keyof によって返される型は string|number|symbol
であるため、状況によっていずれかの型だけが必要な場合は、クロスタイプ書き込み方式を使用できます。
type Capital<T extends string> = Capitalize<T>;
type MyKeys<Obj extends object> = Capital<keyof Obj> // エラー レポート
上記の例では、型 Capital
は型パラメータとして文字列のみを受け入れます。keyof Obj
を渡すと、このときの型パラメータが string|number|symbol
であるため、エラーが報告されます。 、文字列と互換性がありません。以下のクロスタイプ書き込み方法を使用した場合、エラーは報告されません。
type MyKeys<Obj extends object> = Capital<string & keyof Obj>;
上記の例では、string & keyof Obj
は交差演算の string & string|number|symbol
と同等であり、最終的に string
を返すため、Capital<T extends string>
はエラーを報告しません。
オブジェクトのプロパティ名がインデックス形式の場合、keyof はプロパティ名のインデックス タイプを返します。
// 例 1
インターフェース T {
[プロパティ: 数値]: 数値;
}
// 番号
KeyT = keyof T と入力します。
//例2
インターフェース T {
[プロパティ: 文字列]: 数値;
}
// 文字列|数値
KeyT = keyof T と入力します。
上記の 2 番目の例では、keyof T
によって返される型は string|number
です。これは、JavaScript 属性名が文字列の場合、数値属性名が自動的に設定されるため、属性名に数値が含まれるためです。文字列に変換されます。
keyof 演算子が配列またはタプル型で使用される場合、予期しない結果が生じる可能性があります。
type Result = keyof ['a', 'b', 'c'];
// 数値 "0" "2" |
// | "長さ" | "プッシュ" |
上記の例では、 keyof は、数値キー名と継承されたキー名を含む、配列のすべてのキーを返します。
共用体型の場合、keyof はメンバーによって共有されるキー名を返します。
タイプ A = { a: 文字列; z: ブール値 };
タイプ B = { b: 文字列; z: ブール値 };
//「z」を返します
タイプ KeyT = keyof (A | B);
交差タイプの場合、keyof はすべてのキー名を返します。
タイプ A = { a: 文字列; x: ブール値 };
タイプ B = { b: 文字列; y: 数値 };
// 'a' | 'b' を返します。
タイプ KeyT = keyof (A & B);
// と同等
キーの (A & B) ≡ キーの A | キーの B
keyof が抽出するのはキー名からなる共用体型です。 キー値からなる共用体型を抽出したい場合は次のように記述します。
タイプ MyObj = {
foo: 数値、
バー: 文字列、
};
タイプ Keys = keyof MyObj;
type Values = MyObj[Keys] // 数値|文字列;
上記の例では、Keys
はキー名で構成される共用体型であり、MyObj[Keys]
は各キー名に対応するキー値型を取り出して、新しい共用体型、つまり number|string
を形成します。
keyof 演算子の目的
keyof 演算子は、オブジェクトの属性タイプを正確に表現するためによく使用されます。
たとえば、オブジェクトの指定されたプロパティの値を取得するには、JavaScript バージョンを次のように記述できます。
関数 prop(obj, key) {
obj[キー]を返します;
}
上記の関数は型を追加するため、次のようにのみ記述できます。
関数 prop(
obj: { [p:string]: 任意 },
キー: 文字列
):どれでも {
obj[キー]を返します;
}
上記の型宣言には 2 つの問題があります。第 1 に、パラメータ key
とパラメータ obj
の関係を表現できないことです。第 2 に、戻り値の型は any
としてしか記述できません。
keyofを使用すると、この2つの問題が解決され、戻り値の型を正確に表現できるようになります。
function prop<Obj, K extends keyof Obj>(
オブジェクト:オブジェクト、キー:K
):Obj[K] {
obj[キー]を返します;
}
上記の例で、「K extends keyof Obj」は、「K」が「Obj」の属性名であることを意味し、他の文字列が渡されるとエラーが報告されます。戻り値の型「Obj[K]」は、属性値「K」の型を表します。
keyof のもう 1 つの用途は、属性マッピング、つまり、ある型のすべての属性を他の値に 1 つずつマッピングすることです。
type NewProps<Obj> = {
[keyof Obj のプロパティ]: ブール値;
};
// 使用法
タイプ MyObj = { foo: 数値 };
// { foo: boolean } に等しい
タイプ NewObj = NewProps<MyObj>;
上記の例では、型 NewProps
は型 Obj
のマッピング型です。前者は後者のすべての属性を継承しますが、すべての属性の値の型を boolean
に変更します。
次の例では、readonly 修飾子を削除します。
type Mutable<Obj> = {
-readonly [keyof Obj の Prop]: Obj[Prop];
};
// 使用法
タイプ MyObj = {
読み取り専用 foo: 数値;
}
// { foo: 数値 } に等しい
タイプ NewObj = Mutable<MyObj>;
上記の例では、[Prop in keyof Obj]
は Obj
タイプのすべてのプロパティの名前であり、-readonly
はこれらのプロパティの読み取り専用特性を削除することを意味します。これに対応して、読み取り専用の属性設定を追加する書き込みメソッド +readonly
もあります。
次の例では、オプションのプロパティを必須のプロパティにします。
type Concrete<Obj> = {
[keyof Obj のプロパティ]-?: Obj[Prop];
};
// 使用法
タイプ MyObj = {
foo?: 数値;
}
// { foo: 数値 } に等しい
タイプ NewObj = Concrete<MyObj>;
上記の例では、「[Prop in keyof Obj]」の後の「-?」は、オプションの属性設定を削除することを意味します。これに対応して、オプションの属性設定を追加することを意味する「+?」という記述方法もあります。
演算子内
JavaScript 言語では、オブジェクトに特定のプロパティ名が含まれているかどうかを判断するために「in」演算子が使用されます。
const obj = { a: 123 };
if (obj の「a」)
console.log('見つかった');
上記の例では、「in」演算子を使用して、オブジェクト「obj」に属性「a」が含まれているかどうかを判断します。
「in」演算子の左側は属性名を表す文字列で、右側はオブジェクトです。戻り値はブール値です。
TypeScript 言語の型操作では、「in」演算子にはさまざまな用途があり、共用体型の各メンバー型を抽出 (トラバース) するために使用されます。
タイプ U = 'a'|'b'|'c';
タイプ Foo = {
[U のプロップ]: 数値;
};
// と同等
タイプ Foo = {
a:数字、
b: 数字、
c: 数字
};
上の例では、[Prop in U]
は共用体型 U
の各メンバーを順番に取り出すことを意味します。
前のセクションの例では、[Prop in keyof Obj]
がオブジェクト Obj
の各キー名を取得することを意味すると述べました。
角括弧演算子
角括弧演算子 ([]
) は、オブジェクトのキー値の型を取得するために使用されます。たとえば、T[K]
は、オブジェクト T
の属性 K
の型を返します。
タイプ人 = {
年齢: 番号;
名前: 文字列;
生きている: ブール値;
};
// Age の型は数値です
type Age = 人['年齢'];
上の例では、person['age']
は属性 age
のタイプを返します。この場合は number
です。
角括弧のパラメータが共用体型の場合、返される型も共用体型になります。
タイプ人 = {
年齢: 番号;
名前: 文字列;
生きている: ブール値;
};
// 数値|文字列
type T = 人物['年齢'|'名前'];
// 数値|文字列|ブール値
type A = 人[人のキー];
上記の例では、角かっこには属性名の共用体タイプが含まれているため、返されるものは対応する属性値の共用体タイプでもあります。
存在しないプロパティにアクセスすると、エラーが報告されます。
type T = Person['notExisted']; // エラーレポート
角括弧演算子の引数には、プロパティ名のインデックス タイプを指定することもできます。
型オブジェクト = {
[キー:文字列]: 数値、
};
// 番号
type T = Obj[文字列];
上記の例では、Obj
の属性名は文字列のインデックス型であるため、すべての文字列属性名を表す Obj[string]
と書くことができ、その型 number
が返されます。
この構文は、角括弧の引数として「number」を使用する配列に対しても機能します。
// MyArray の型は { [key:number]: string } です
const MyArray = ['a','b','c'];
// (typeof MyArray)[number] と同等
//文字列を返す
type Person = typeof MyArray[数値];
上記の例では、MyArray
は配列であり、その型は実際には属性名の数値インデックスであり、typeof MyArray[number]
の typeof
操作の優先順位は角括弧の優先順位よりも高いため、すべての数値キーが返されます。キー値の型は名前の string
です。
角括弧内では値の演算はできないことに注意してください。
// 例 1
const key = '年齢';
type Age = 人[キー] // エラーレポート
//例2
type Age = Person['a' + 'g' + 'e']; // エラーレポート
上の 2 つの例では、角括弧に値の操作が含まれています。この操作はコンパイル中に実行されないため、エラーが報告されます。
extends...?: 条件演算子
TypeScript は、JavaScript の ?:
演算子に似た三項演算子を提供しますが、追加の extends
キーワードが付いています。
条件演算子 extends...?:
は、現在の型が特定の条件を満たすかどうかに基づいて、さまざまな型を返すことができます。
T は U X を拡張しますか?
上の式の extends
は、型 T
を型 U
に割り当てることができるかどうか、つまり、T
が T
のサブタイプであり、ここでの U
が割り当てられるかどうかを判断するために使用されます。何でもタイプ。
T
を型 U
に代入できる場合、式の結果は型 X' になります。それ以外の場合、結果は型 'Y
になります。
// 真実
type T = 1 は数値を拡張します true : false;
上記の例では、「1」は「number」のサブタイプであるため、「true」が返されます。
別の例を示します。
インターフェース 動物 {
live(): 無効;
}
インターフェース Dog 拡張 Animal {
ワンワン(): 無効;
}
// 番号
type T1 = 犬は動物の数を拡張します : 文字列;
// 弦
type T2 = RegExp extends Animal 数値 : 文字列;
上記の例では、「Dog」は「Animal」のサブタイプであるため、「T1」のタイプは「number」になります。 RegExp
は Animal
のサブタイプではないため、T2
の型は string
です。
一般に、「extends」の両側の型を交換すると、逆の結果が返されます。たとえば、「Cat」と「Animal」という 2 つのクラスがあり、前者が後者のサブタイプである場合、「Cat extends Animal」は true、「Animal extends Cat」は false になります。
判定対象の型が共用体型の場合、条件演算子は共用体型を展開します。
(A|B) U X : Y を拡張します。
// と同等
(A は U ? X : Y を拡張します) |
(B は U ? X : Y を拡張します)
上記の例では、A|B
は共用体型であり、条件付き演算を実行する場合、それはそれぞれ A
と B
に対して演算子を実行することと同等であり、返される結果は共用体型を形成します。
条件演算子によって共用体型を展開したくない場合は、「extends」の両側のオペランドを角かっこで囲むことができます。
// 例 1
type ToArray<Type> =
Type は任意の Type[] を拡張します。
// 文字列[]|数値[]
type T = ToArray<文字列|数値>;
//例2
type ToArray<Type> =
[タイプ] は [任意の] タイプ [] を拡張しますか?
// (文字列 | 数値)[]
type T = ToArray<文字列|数値>;
上記の例 1 では、ToArray<Type>
に渡される type パラメータは共用体型であるため、展開され、返される型も共用体型になります。例 2 では、「extends」の両側のオペランドが角かっこで囲まれているため、渡された共用体型は展開されず、配列が返されます。
条件演算子はネストすることもできます。
type LiteralTypeName<T> =
Tは未定義を拡張しますか? "未定義":
T は null "null" を拡張します:
T はブール値を拡張しますか?
T は数値 ? "数値" を拡張します:
T は bigint を拡張しますか?
T は文字列 "文字列" を拡張します:
一度もない;
上記の例は多重判定であり、現在の型に対応する文字列の値の型を返します。使用方法は次のとおりです。
// "bigint"
タイプ Result1 = リテラルタイプ名<123n>;
// "文字列" | "数値" |
タイプ Result2 = リテラルタイプ名<true 1 |
キーワードを推測します
infer
キーワードは、外部から渡される型パラメータではなく、ジェネリックスから推論される型パラメータを定義するために使用されます。
これは通常、親タイプの extends
キーワードの後に条件演算子と一緒に使用されます。
type Flatten<Type> =
Type extends Array<infer item> : Type;
上記の例では、「infer Items」は、「Item」パラメータが TypeScript 自体によって推論され、明示的に渡す必要がないことを意味します。一方、「Flatten」は、「Type」型パラメータが外部で渡されることを意味します。 。 「Type extends Array」 は、パラメータ Type
が配列の場合、配列のメンバーの型が Item
として推論される、つまり Item
が Type
から推論されることを意味します。
「Infer Item」を使用して「Item」を定義すると、次のコードで「Item」を直接呼び出すことができます。以下は、上記の例における汎用の Flatten<Type>
の使用法です。
// 弦
type Str = Flatten<string[]>;
// 番号
type Num = Flatten<数値>;
上記の例では、最初の例 Flatten<string[]>
で渡された型パラメータは string[]
です。Item
の型は string
であると推測できるため、戻り値は次のようになります。 「文字列」。 2 番目の例では、Flatten<number>
に渡される type パラメータは number
ですが、これは配列ではないため、それ自体を直接返します。
型パラメータが「infer」を使用して定義されていない場合は、2 つの型パラメータを渡す必要があります。
type Flatten<タイプ, アイテム> =
Type extends Array<Item> : Type;
上記は infer を使わない書き方です。Flatten を呼び出すたびにパラメータを 2 つ渡さなければならず、面倒です。
次の例では、「infer」を使用して関数のパラメーターの型と戻り値の型を推測します。
型 ReturnPromise<T> =
T extends (...args: Aを推測) => Rを推測
? (...引数: A) => 約束<R>
:T;
上の例では、「T」が関数の場合、この関数の Promise バージョンが返され、それ以外の場合は変更されずに返されます。 「infer A」は関数のパラメータの型が「A」であることを意味し、「infer R」は関数の戻り値の型が「R」であることを意味します。
infer
を使用しない場合、ReturnPromise<T>
を ReturnPromise<T, A, R>
として記述する必要がありますが、これは非常に面倒であり、開発者がコンパイラができることを手動で推論しなければならないのと同じです。 。
以下は、オブジェクトの指定されたプロパティを抽出する「infer」の例です。
タイプ MyType<T> =
T は { を拡張します
a: M を推測します。
b: N を推測する
[M, N] : 決して;
// 使用例
type T = MyType<{ a: 文字列; b: 数値 }>;
// [文字列、数値]
上記の例では、「infer」によりパラメータオブジェクトの属性「a」と属性「b」の型が抽出されます。
以下は、通常のマッチングによって型パラメータを抽出する「infer」の例です。
タイプ Str = 'foo-bar';
type Bar = Str extends `foo-${inferrest}` ?rest : none // 'bar'
上記の例では、「rest」はテンプレート文字列から抽出された型パラメータです。
は演算子です
関数がブール値を返す場合、「is」演算子を使用して戻り値とパラメータの間の関係を制限できます。
is
演算子は、戻り値が true
に属するか false
に属するかを記述するために使用されます。
関数 isFish(
ペット: 魚|鳥
):ペットは魚です{
return (魚としてペット).swim !== 未定義;
}
上記の例では、関数 isFish()
の戻り値の型は pet is Fish
です。これは、パラメータ pet
が Fish
型の場合は true
を返し、それ以外の場合は を返すことを意味します。偽
。
is
演算子は、関数の戻り値の型を記述するために常に使用されます。これは、parameterName is Type
の形式で記述されます。つまり、左側は現在の関数のパラメーター名であり、右側は です。あるタイプ。左側のパラメータが右側のタイプであるかどうかを示すブール値を返します。
type A = { a: 文字列 };
type B = { b: 文字列 };
関数 isTypeA(x: A|B): x は A {
if (x の「a」) は true を返します。
false を返します。
}
上記の例では、戻り値の型「x is A」により、関数本体内の演算ロジックを正確に記述することができます。
is
演算子は型保護に使用できます。
function isCat(a:any): a は Cat {
return a.name === 'キティ';
}
x:猫|犬とします。
if (isCat(x)) {
x.meow(); // 正解です。x は Cat 型である必要があります。
}
上記の例では、関数 isCat()
の戻り値の型は a is Cat
であり、ブール値です。次の if
ステートメントは、この戻り値を判断に使用し、x
が Cat 型であることを保証する型保護の役割を果たします。そのため、x.meow()
はエラーを報告しません (Cat
であると仮定します)。 type には meow()
メソッドがあります)。
「is」演算子の特別な使用法もあります。これは、クラスメソッドの戻り値を記述するためにクラス内で使用されます。
クラスの先生{
isStudent():これは学生です {
false を返します。
}
}
クラス学生{
isStudent():これは学生です {
true を返します。
}
}
上記の例では、isStudent()
メソッドの戻り値の型は、メソッド内の this
が Student
オブジェクトであるかどうかによって異なります。はいの場合はブール値「true」を返し、そうでない場合は「false」を返します。
「this is T」はメソッドの戻り値の型を記述するためにのみ使用でき、属性の型を記述するためには使用できないことに注意してください。
テンプレート文字列
TypeScript では、テンプレート文字列を使用して型を構築できます。
テンプレート文字列の最大の特徴は、内部で他の型を参照できることです。
World = "ワールド" と入力します。
// "こんにちは世界"
type Greeting = `hello ${World}`;
上記の例では、型 Greeting
は別の文字列型 world
を参照するテンプレート文字列であるため、Greeting
は実際には文字列 hello world
になります。
テンプレート文字列が参照できる型は、string、number、bigint、boolean、null、unknown、および Enum の合計 7 つであることに注意してください。これら 7 つ以外の型を参照するとエラーになります。
タイプ番号 = 123;
タイプ Obj = { n : 123 };
type T1 = `${Num} を受け取りました` // 正しい
type T2 = `${Obj} を受信しました` // エラーレポートを受け取りました
上記の例では、テンプレート文字列が数値型のエイリアス Num
を参照することは問題ありませんが、オブジェクト型のエイリアス `Obj' を参照するとエラーが報告されます。
テンプレート文字列で参照される型が共用体型の場合、共用体型も返します。つまり、テンプレート文字列は共用体型を展開できます。
タイプ T = 'A'|'B';
// "A_id"|"B_id"
タイプ U = `${T}_id`;
上記の例では、型 U
は共用体型 T
を参照するテンプレート文字列であるため、最終結果も共用体型になります。
テンプレート文字列が 2 つの共用体型を参照する場合、2 つの型を相互展開します。
タイプ T = 'A'|'B';
タイプ U = '1'|'2';
// 'A1'|'A2'|'B1'|'B2'
タイプ V = `${T}${U}`;
上記の例では、T
と U
はどちらも共用体型であり、それぞれ 2 つのメンバーを持ちます。これら 2 つの型はテンプレート文字列で参照され、最終的な結果は 4 つのメンバーを持つ共用体型になります。
は演算子を満たす
「satisfies」演算子は、値が指定された型に準拠しているかどうかをチェックするために使用されます。特定の値を特定の型として指定することが不便な場合がありますが、この場合、値が型の条件を満たすことが望ましい場合は、「satisfies」演算子を使用してそれを検出できます。 TypeScript 4.9 では、この演算子が追加されました。
たとえば、オブジェクトのプロパティ名のスペルが間違っているとします。
const パレット = {
赤: [255, 0, 0]、
緑: "#00ff00",
bleu: [0, 0, 255] // プロパティ名のスペルが間違っています
};
上の例では、オブジェクト「palette」の属性名のスペルが間違っています。「blue」のスペルは「bleu」です。タイプを指定することで、このエラーを見つけることができます。
色 = "緑" | を入力します。
タイプ RGB = [数値、数値、数値];
const パレット: Record<Colors, string|RGB> = {
赤: [255, 0, 0]、
緑: "#00ff00",
bleu: [0, 0, 255] // エラーレポート
};
上記の例では、変数 palette
の型は、オブジェクトを返すために使用される型ツールである Record<Colors, string|RGB>
として指定されています。詳細については、「型ツール」の章を参照してください。簡単に言えば、最初の型パラメータはオブジェクトの属性名を指定し、2 番目の型パラメータはオブジェクトの属性値を指定します。
この例の Record<Colors, string|RGB>
は、変数 palette
の属性名が Colors
型に準拠し、属性値が string|RGB
型に準拠する必要があることを意味します。文字列またはタプル RGB
。属性名 bleu
はタイプ Colors
と一致しないため、エラーが報告されます。
この書き方では属性名のスペルミスを検出できますが、新たな問題も引き起こします。
const greenComponent = pattern.green.substring(1, 6); // エラーレポート
上の例では、palette.green
プロパティで substring()
メソッドを呼び出すとエラーが報告されます。その理由は、このメソッドは文字列に対してのみ使用可能であり、palette.green
の型は srting' であるためです。 |RGB
は文字列のほかにタプル RGB
もありますが、タプルには substring()
メソッドが存在しないため、エラーが報告されます。
エラーを避けたい場合は、変数 palette
の各属性の型を正確に指定するか、palette.green
の値を絞り込んで入力してください。どちらのアプローチも面倒であり、あまり必要ありません。
現時点では、「satisfies」演算子を使用して「palette」の型検出を実行できますが、「palette」の TypeScript の型推論は変更されません。
色 = "緑" | を入力します。
タイプ RGB = [数値、数値、数値];
const パレット = {
赤: [255, 0, 0]、
緑: "#00ff00",
bleu: [0, 0, 255] // エラーレポート
Record<Colors, string|RGB> を満たします。
const greenComponent = pattern.green.substring(1); // エラーは報告されませんでした。
上記の例では、変数 palette
の値の後に satisfies Record<Colors, string|RGB>
が追加されています。これは、値が Record<Colors, string|RGB>
の条件を満たす必要があることを意味します。属性名「bleuのスペルミス」を検出できます。同時に、palette
の型推論は変更されないため、TypeScript は palette.green
が文字列であることを認識し、その上で substring()
メソッドを呼び出してもエラーは報告されません。
「satisfies」は属性値を検出することもできます。
const パレット = {
赤: [255, 0, 0]、
緑: "#00ff00",
blue: [0, 0] // エラーを報告する
Record<Colors, string|RGB> を満たします。
上記の例では、属性 blue
の値にはメンバーが 2 つしかなく、タプル RGB
には 3 つのメンバーが必要という条件を満たしていないため、エラーが報告されます。
作者: wangdoc
アドレス: https://wangdoc.com/
ライセンス: クリエイティブ・コモンズ 3.0