JSDocでも使うので、型が書けないと実践的なJavaScriptも書けないと思います。
でもめちゃくちゃめんどくさいですよね。
TypeScriptを書き始めのころはなんでもかんでも型を書いちゃうし、型が分からなくてanyにしちゃったりしますよね。
ここではできるだけ型を書かなくてもよくする方法をまとめます。
基本
type TypeA = {
type: "a";
name: "hoge" | "bar";
value: string;
}
type TypeB = {
type: "b";
id: "hoge" | "bar";
value: 0;
}
//複合的な型(TypeAかTypeBのどちらか)
type ConbinedType = TypeA | TypeB;
//結合された型(TypeAであり同時にTypeBである)
type ConnectedType = TypeA & TypeB;
/* 型推論 */
type Example = {
a: string;
b: string;
}
//プロパティの型を取得する。
type ExampleType = Example['a'];
const array : Example[] = [{
a: 'a',
b: 'b',
} , {
a: 'A',
b: 'B',
}];
///すごく長いコード......///
//arrayのメンバーの型定義がソースコード上深すぎてわからないとき、型推論から型を取り出す。
type ArrayMemberType = typeof array[0];
/* クラスの型 */
class Processor {
process(){
return 'done';
}
}
//クラスの型を取得する
type ProcessorType = typeof Processor;
//クラスのメソッドの型を取得する
type ProcessFunctionType = ProcessorType['prototype']['process'];
const instance = new Processor();
//インスタンス化されているクラス内の関数の型を取得する。
type InstanceProcessFunctionType = typeof instance.process;
戻り値(ReturnType)
//例えば初期値を定義する関数があるとする。
function createInit(){
const initData : {
id : string;
name : string;
} = {
id : "hoge",
name : "fuga"
}
return initData;
}
//関数の構造を型として定義する
export type CreaterType = typeof createInit;
//関数の戻り値の型をデータ型として定義して使う
export type AppState = ReturnType<CreaterType>;
非同期処理(Awaited)
//文字列や数値が非同期に帰ってくる関数。
async function asyncRandom() {
if (Math.random() > 0.5) {
return 'done';
}else{
return 0;
}
}
//() => Promise<"done" | 0>
type AsyncType = typeof asyncRandom;
//戻り値を表現する型
type AsyncReturnType = Awaited<ReturnType<AsyncType>>;
// -> "done" | 0
複数の型を処理できるようにする
type TypeA = {
//型を複合して使う場合、分類用のプロパティを用意しておく
type : "a";
name : "hoge" | "bar";
value : string;
}
type TypeB = {
//型を複合して使う場合、分類用のプロパティを用意しておく
type : "b";
id : "hoge" | "bar";
value : 0;
}
//テストデータ
const data : (TypeA | TypeB)[] = [{
type : "a",
name : "hoge",
value : "fuga"
}, {
type : "b",
id : "hoge",
value : 0
}];
for(let datum of data){
//複合的な型から、型推論を利用して型を矯正する
if("a" == datum.type){
//nameはTypeAのプロパティなので、TypeBには存在しない
console.log(datum.name);
}else{
//idはTypeBのプロパティなので、TypeAには存在しない
console.log(datum.id);
}
}
プロパティの算法(Partial, Pick, Omit)
type ManyProperty = {
a : string;
b : string;
c : string;
d : string;
e : string;
}
//"a"しかプロパティがない型
type OnlyA = Pick<ManyProperty, 'a'>;
/* ->
type OnlyA = {
a: string;
}
*/
//すべてのプロパティが必須ではないかもしれない型
type PatialManyProperty = Partial<ManyProperty>;
/* ->
type PatialManyPropertye = {
a?: string | undefined;
b?: string | undefined;
c?: string | undefined;
d?: string | undefined;
e?: string | undefined;
}
*/
//すべてのプロパティが必須になった型
type RequiredManyProperty = Required<PatialManyProperty>;
/* ->
type RequiredManyProperty = {
a: string;
b: string;
c: string;
d: string;
e: string;
}
*/
//"a"と"b"をプロパティに含まない型
type NotInclueAB = Omit<ManyProperty, 'a' | 'b'>;
/* ->
type NotInclueAB = {
c: string;
d: string;
e: string;
}
*
KintoneRestAPIClientを利用するときの型推論
インポートする。
npm i @kintone/rest-api-client
import type { KintoneRestAPIClient } from '@kintone/rest-api-client';
//getFormFieldsの戻り値の型を取得する。
type FormResponse = Awaited<ReturnType<typeof KintoneRestAPIClient['prototype']['app']['getFormFields']>>;
/*
type FormResponse = {
properties: Properties;
revision: string;
}
*/
//レコード取得したときの型(あんまり意味ないけど)
type RecordResponse = Awaited<ReturnType<typeof KintoneRestAPIClient['prototype']['record']['getAllRecords']>>;
//kintoneのフォームに定義できる型(typeプロパティ)の一覧
type FieldType = FormResponse['properties'][string]['type'];
このように、stringではなく、明示的な定数で定義されるので、switch文などの展開で安全にコーディングをすすめられます。
まとめ
TypeScriptでは型演算と型推論を利用し、処理を書いている間はほぼ型定義なしで書き進められます。
型を利用できるようになると、開発環境のインテリセンス(サジェストを表示する機能)を利用して安全で高速に開発ができます。体感で倍以上は開発速度が違うと思います。
みなさまも、よく使うやり方があったら教えてください!
この記事は以上です。ありがとうございました。
kintoneのプラグイン開発や研修などを行っています。
お仕事のお話はこちらまで。