JavaScript、TypeScriptで値を扱うとき、let
を用いて宣言を行うと可読性が著しく下がるので可能な限りconst
を利用して宣言したいです。
この信条をもとに開発を行うと、宣言する値が分岐する場合でもconst
で定義することになります。そのように作られたコードは以下のように可読性が下がります(この例は分岐の階層が1なのでまだマシで、階層が増えるとさらに見にくいコードになります)。
const colorSchema = (
type === 'info'
? ({
color: '#0288d1',
backgroundColor: '#03a9f4',
} as const)
: type === 'success'
? ({
color: '#2e7d32',
backgroundColor: '#4caf50',
} as const)
: type === 'warning'
? ({
color: '#ed6c02',
backgroundColor: '#ff9800',
} as const)
: type === 'error'
? ({
color: 'd32f2f',
backgroundColor: '#ef5350',
} as const)
: ({
color: 'inherit',
backgroundColor: 'inherit',
} as const)
) satisfies ColorSchema;
そのため、分岐が複雑な場合はその可読性の低下がlet
を使うことによる可読性の低下よりも大きいと考えられ、以下のようにlet
を使って宣言することが多いです。
let colorSchema: ColorSchema;
switch (type) {
case 'info':
colorSchema = {
color: '#0288d1',
backgroundColor: '#03a9f4',
} as const;
break;
case 'success':
colorSchema = {
color: '#2e7d32',
backgroundColor: '#4caf50',
} as const;
break;
case 'warning':
colorSchema = {
color: '#ed6c02',
backgroundColor: '#ff9800',
} as const;
break;
case 'error':
colorSchema = {
color: 'd32f2f',
backgroundColor: '#ef5350',
} as const;
break;
default:
colorSchema = {
color: 'inherit',
backgroundColor: 'inherit',
} as const;
break;
}
switch
文による分岐になりましたので、どの分岐でどのような値が代入されているか分かりやすくなりました。この形式ではlet
を使っているので今後変更されるかもしれないことを頭の片隅に入れて読んでいく必要があります。
さらに、colorSchema
の型はこれだけ、実際に取りうるオブジェクトのユニオンではなくColorSchema
と推論されます。この例では対した問題にはなりませんが、オブジェクトが持つ値によって分岐を作るときに型を絞ること(制御フローによる型の絞り込み)が出来ないという面が劣っています。
これらを解決するのが分岐処理を関数で行わせてその結果を値として宣言するという方法です。
const colorSchema = (() => {
switch (type) {
case 'info':
return {
color: '#0288d1',
backgroundColor: '#03a9f4',
} as const;
case 'success':
return {
color: '#2e7d32',
backgroundColor: '#4caf50',
} as const;
case 'warning':
return {
color: '#ed6c02',
backgroundColor: '#ff9800',
} as const;
case 'error':
return {
color: 'd32f2f',
backgroundColor: '#ef5350',
} as const;
default:
return {
color: 'inherit',
backgroundColor: 'inherit',
} as const;
}
})() satisfies { color: string; backgroundColor: string };
このようにすることで、分岐による可読性を保ったままconst
で定義することが出来ます(型も詳細なオブジェクトのユニオンが得られます)。汎用的な分岐処理であれば都度無名関数で作成するのではなく、関数として宣言して使い回すようにしても良いと思います。