Quantcast
Channel: 日本語訳タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 236

TypeScriptの型上級チートシート

$
0
0

Original article: https://www.ibrahima-ndaw.com/blog/advanced-typescript-cheat-sheet/

以下はIbrahima Ndaw( Twitter / GitHub / LinkedIn / Webサイト )によるTypeScriptの解説、Advanced TypeScript Types cheat sheet (with examples)の日本語訳です。
リンクなどは元記事のままであり、和訳にあたり変更していません。

Advanced TypeScript Types cheat sheet (with examples)

TypeScriptは型付き言語であり、変数、関数の引数および返り値、オブジェクトのプロパティに型を指定することが可能です。

この記事では、TypeScriptの型の高度な使い方を例示付きで紹介します。

Sorry for the interrupt!

TypeScriptを総合的に学びたい人には、こちらのベストセラーコースを強くお勧めします。
Understanding TypeScript - 2020 Edition
これはアフィリエイトリンクなので、よかったら応援してね。

Intersection Types

交差型とは、複数の型をひとつに結合した型です。
すなわち、型Aと型B、もしくはさらに他の型をマージして、それら全てのプロパティを持ったひとつの型を得ることができます。

typeLeftType={id:numberleft:string}typeRightType={id:numberright:string}typeIntersectionType=LeftType&RightTypefunctionshowType(args:IntersectionType){console.log(args)}showType({id:1,left:"test",right:"test"})// Output: {id: 1, left: "test", right: "test"}

見てのとおり、交差型はLeftTypeとRightType両方の要素を持っています。
交差型をつくるには&で結合するだけです。

Union Types

Union型は、与えられた型のうち何れかの型となることができます。

typeUnionType=string|numberfunctionshowType(arg:UnionType){console.log(arg)}showType("test")// Output: testshowType(7)// Output: 7

関数showTypeは、string型もしくはnumber型いずれかの値を引数として受け付けることができます。

Generic Types

ジェネリック型とは、与えられた型を再利用する手段です。
引数の型を変数のようにキャプチャすることができます。

functionshowType<T>(args:T){console.log(args)}showType("test")// Output: "test"showType(1)// Output: 1

ジェネリック型を生成するには、関数名に<>で括った型名T ( 実際は任意の名前でよい ) を指定します。
以下に、関数showTypeを異なる型で呼び出す例を示します。

interfaceGenericType<T>{id:numbername:T}functionshowType(args:GenericType<string>){console.log(args)}showType({id:1,name:"test"})// Output: {id: 1, name: "test"}functionshowTypeTwo(args:GenericType<number>){console.log(args)}showTypeTwo({id:1,name:4})// Output: {id: 1, name: 4}

ジェネリック型Tを受け取るインターフェイスGenericTypeを定義しました。
これは再利用可能なので、ひとつめのGenericTypeはstring型の値を受け取り、ふたつめはnumber型を受け取っています。

interfaceGenericType<T,U>{id:Tname:U}functionshowType(args:GenericType<number,string>){console.log(args)}showType({id:1,name:"test"})// Output: {id: 1, name: "test"}functionshowTypeTwo(args:GenericType<string,string[]>){console.log(args)}showTypeTwo({id:"001",name:["This","is","a","Test"]})// Output: {id: "001", name: Array["This", "is", "a", "Test"]}

ジェネリック型を複数渡すこともできます。
上の例では二つのジェネリック型TとUを渡しています。
interfaceを使用することで、異なる型の引数を渡す関数が提供できるようになりました。

Utility Types

TypeScriptでは、型を容易に操作することができるように便利な組込ユーティリティ型が提供されています。
これらを使うときは、変換したい型を<>に入れて渡します。

Partial

Partial<T>

Partial型は、該当する型の全てのプロパティをオプショナルにすることができます。
これはすなわち、全てのフィールドに?を追加するようなものです。

interfacePartialType{id:numberfirstName:stringlastName:string}// firstNameがstringからstring?になるfunctionshowType(args:Partial<PartialType>){console.log(args)}showType({id:1})// Output: {id: 1}showType({firstName:"John",lastName:"Doe"})// Output: {firstName: "John", lastName: "Doe"}

関数showType()の引数としてPartialType型を渡していますが、プロパティをオプショナルにするためにPartialユーティリティ型を通しています。
これだけで、PartialType型の全ての値がオプショナルになりました。

Required

Required<T>

Partial型とは「逆に、Required型は全てのプロパティを必須にします。

interfaceRequiredType{id:numberfirstName?:stringlastName?:string}// firstNameがstring?からstringになるfunctionshowType(args:Required<RequiredType>){console.log(args)}showType({id:1,firstName:"John",lastName:"Doe"})// Output: { id: 1, firstName: "John", lastName: "Doe" }showType({id:1})// Error: Type '{ id: number: }' is missing the following properties from type 'Required<RequiredType>': firstName, lastName

Requiredユーティリティ型を通すことによって、オプショナルであるはずのRequiredType型の全ての値が必須になります。
プロパティを省略した場合、TypeScriptはエラーを発生させます。

Readonly

Readonly<T>

Readonlyユーティリティ型は、全てのプロパティを変更不可能にします。

interfaceReadonlyType{id:numbername:string}functionshowType(args:Readonly<ReadonlyType>){args.id=4console.log(args)}showType({id:1,name:"Doe"})// Error: Cannot assign to 'id' because it is a read-only property.

Readonlyユーティリティ型によって、ReadonlyType型の全ての値は再割り当て不能になります。
いずれかのフィールドに新しい値を設定しようとすると、エラーになります。

もっと単純に、プロパティの前にreadonlyキーワードを付けて再割り当て不能にすることもできます。

interfaceReadonlyType{readonlyid:numbername:string}

Pick

Pick<T, K>

Pickユーティリティ型は、元の型からいくつかのプロパティを選んで新たな型を生成します。

interfacePickType{id:numberfirstName:stringlastName:string}// PickTypeのうちfirstName,lastNameだけを使った新たな型functionshowType(args:Pick<PickType,"firstName"|"lastName">){console.log(args)}showType({firstName:"John",lastName:"Doe"})// Output: {firstName: "John"}showType({id:3})// Error: Object literal may only specify known properties, and 'id' does not exist in type 'Pick<PickType, "firstName" | "lastName">'

これまでに見てきたユーティリティ型とは少々異なる構文で、二つの引数が必要です。
Tは元の型、そしてKは抽出したいプロパティです。
複数のフィールドを|で区切ることによって、複数のフィールドを抽出することも可能です。

Omit

Omit<T, K>

Omitユーティリティ型はPickのちょうど反対で、必要なプロパティを選ぶのではなく不要なプロパティを削除します。

interfacePickType{id:numberfirstName:stringlastName:string}// PickTypeのうちfirstName,lastNameを使わない新たな型functionshowType(args:Omit<PickType,"firstName"|"lastName">){console.log(args)}showType({id:7})// Output: {id: 7}showType({firstName:"John"})// Error: Object literal may only specify known properties, and 'firstName' does not exist in type 'Pick<PickType, "id">'

Pickと同じ使い方で、元となる型から削除するプロパティを指定します。

Extract

Extract<T, U>

Extractユーティリティ型は、T型のプロパティのうち、U型に代入可能なプロパティを抽出します。
2つの型に共通するプロパティを取り出すと考えてよいでしょう。

interfaceFirstType{id:numberfirstName:stringlastName:string}interfaceSecondType{id:numberaddress:stringcity:string}typeExtractType=Extract<keyofFirstType,keyofSecondType>// Output: "id"

上の例では、2つの型が同じプロパティidを持っています。
この型にExtractを使用することで、両方に共通するプロパティidを取り出すことができます。
共通するプロパティが複数存在する場合は、その全てが抽出されます。

Exclude

Exclude<T, U>

Excludeユーティリティ型は、T型のプロパティのうち、U型に代入可能なプロパティを除外した型を生成します。

interfaceFirstType{id:numberfirstName:stringlastName:string}interfaceSecondType{id:numberaddress:stringcity:string}typeExcludeType=Exclude<keyofFirstType,keyofSecondType>// Output; "firstName" | "lastName"

FirstTypeのプロパティfirstNamelastNameはSecondTypeには存在しないため、Excludeで取り出すことができます。
SecondTypeの値addresscityは出てきません。

Record

Record<K,T>

このユーティリティは、T型の値の集合を作るために役立ちます。
ある型のプロパティを別の型にマッピングする際に、非常に便利です。

interfaceEmployeeType{id:numberfullname:stringrole:string}letemployees:Record<number,EmployeeType>={0:{id:1,fullname:"John Doe",role:"Designer"},1:{id:2,fullname:"Ibrahima Fall",role:"Developer"},2:{id:3,fullname:"Sara Duckson",role:"Developer"},}// 0: { id: 1, fullname: "John Doe", role: "Designer" },// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }

Recordの動作はシンプルです。
上の例ではキーの型がnumberなので、0,1,2と数値を指定しています。
値はEmployeeType型となっているので、id・fullname・roleを持つオブジェクトが必要です。
文字列を与えたりするとエラーになります。

NonNullable

NonNullable<T>

型Tからnullとundefinedを消し去ります。

typeNonNullableType=string|number|null|undefinedfunctionshowType(args:NonNullable<NonNullableType>){console.log(args)}showType("test")// Output: "test"showType(1)// Output: 1showType(null)// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.showType(undefined)// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.

NonNullableユーティリティは、引数からnullとundefinedを消し去った新たな型を生成します。
その型にNullableな値を渡すと、TypeScriptはエラーを発します。

なお、tsconfigファイルにstrictNullChecksを指定すると、自動的に全ての型にNonNullableが適用されます。

Mapped types

Mapped typesは、既存のモデルを流用しつつ、各プロパティを新しい型に変更することができるようになります。
先に解説したユーティリティ型も、一部の実体はMapped typesです。

typeStringMap<T>={[PinkeyofT]:string}functionshowType(arg:StringMap<{id:number;name:string}>){console.log(arg)}showType({id:1,name:"Test"})// Error: Type 'number' is not assignable to type 'string'.showType({id:"testId",name:"This is a Test"})// Output: {id: "testId", name: "This is a Test"}

StringMap<>は、渡された型が何であれ、とりあえず文字列型にします。
従って、showTypeに渡す型はnumberではなくstringとなり、number型を渡した場合はエラーが出ることになります。

Type Guards

Type Guardsを使うと、変数やオブジェクトの型を演算子で判定することができます。

typeof

functionshowType(x:number|string){if(typeofx==="number"){return`The result is ${x+x}`}thrownewError(`This operation can't be done on a ${typeofx}`)}showType("I'm not a number")// Error: This operation can't be done on a stringshowType(7)// Output: The result is 14

上記コードでは、typeofを用いて受け取った引数の型をチェックしています。
条件で型ガードすることができました。

instanceof

classFoo{bar(){return"Hello World"}}classBar{baz="123"}functionshowType(arg:Foo|Bar){if(arginstanceofFoo){console.log(arg.bar())returnarg.bar()}thrownewError("The type is not supported")}showType(newFoo())// Output: Hello WorldshowType(newBar())// Error: The type is not supported

typeofの例と同様、こちらでは引数の型がFooクラスであるかをチェックします。

in

interfaceFirstType{x:number}interfaceSecondType{y:string}functionshowType(arg:FirstType|SecondType){if("x"inarg){console.log(`The property ${arg.x} exists`)return`The property ${arg.x} exists`}thrownewError("This type is not expected")}showType({x:7})// Output: The property 7 existsshowType({y:"ccc"})// Error: This type is not expected

オブジェクトにプロパティが存在するかどうかはinでチェックすることができます。

Conditional Types

以下では複数の型をテストし、結果に応じてその片方を選択しています。

typeNonNullable<T>=Textendsnull|undefined?never:T

NonNullableユーティリティ型は、型がnullであるかをチェックし、結果に応じて異なる処理をしています。
この例ではJavaScriptの三項演算子を使用していることに注意してください。

コメント欄

dev.toのコメント欄

「わかりやすくてよい記事。GJ!」
「簡潔によくまとめられてる。」
独自のタイプガードの可能性を追求した。」
「もっとはやくPartialを知りたかった。わざわざ自力で書いてたよ。export type ObjectWithOptionalProps<T> = { [key in keyof T]?: T[key] };
「Maybe型type Maybe<T> = T | null;をよく使ってる。」「普通にオプショナルfunction foo(bar?: string)でよくない?」「Maybe型の場合は値が必須というところが異なるよ。」
「TypeScript複雑になりすぎてきたような」「ほとんどは糖衣構文なので使わなければいいだけだぞ」「せやな」
「行末にセミコロンを忘れるな」「セミコロンは必須ではないし使わない方が好き」「ない方がすっきりしててよい」「Google様に逆らうなら不要だね」

感想

TypeScript Guideシリーズの2番目で、主に型の変換について記述された記事です。
他のシリーズも通して読むことで、TypeScriptへの理解がより深まることでしょう。
参考になったらぜひコーヒーを買ってあげましょう。

実際に手を動かしてみたいという場合は、ちょうど最近日本語解説記事が出たtype-challengesなどを試してみるとよいかもしれません。

しかし型に関するいろいろな機能はありますが、実際使うかといったら個人的にはほとんど使っていません。
私はフロントエンドエンジニアではないので本格的にがっつり使ってないからということもありますが、極めてふつーに、型を定義してそれを直接使う程度のことしかやっていませんし、それで不足を感じることもあまりありません。
日頃使ってるのはせいぜいUnion型くらいです。
OmitとかExcludeとか何に使うのか全くわからん。
みんなこういうのバキバキ使いこなしてるんですかね?


Viewing all articles
Browse latest Browse all 236

Trending Articles