🌟

一日一処: TypeScriptで文字列を渡してテンプレートリテラルを用いた型を作る

2024/02/20に公開

テンプレートリテラル

テンプレートリテラルは、バッククォートを使用して、変数展開のできる文字列だ。

const name = 'bob'
console.log(`I'm ${name}`)

非常に便利で、ここには式も入れることができる万能な文字列だ。

型の中にテンプレートリテラルを組み込む

通常文字列の型は、以下のように定義できる。

type Status = 'success'
// OK
const status: Status = 'success'
// NG
const status: Status = 'failed'

例えば、IDや決まった文字列のような物がある時、これがテンプレートリテラルの構造に一致していれば問題なかったりする。例えば、電話番号だ。

type PhoneNumber = `${string}-${string}-${string}`
const number: PhoneNumber = '090-0000-0000'

上記は、文字列で型を定義したが、数値ももちろん問題ない。

type PhoneNumber = `${number}-${number}-${number}`
const number: PhoneNumber = '090-0000-0000'

そのため、単位の表現も問題ない。

type Pixels = `${number}px`
const number: Pixels= '100px'

これを組み合わせれば、より具体的な文言について入力を強制することもできる。

type Intro = `I'm ${string}, ${number} years old.`
const intro: Intro = "I'm Bob, 2024 years old."

番外編

interfaceのプロパティ定義において、0から1までの小数の数字を受け入れるようにしたいというStackoverflowの記事を見つけた。
TypeScriptの仕様を考えれば、値そのものに干渉するような型定義はできないのは当然で、この回答にも、クラスで値を判定する仕組みが提案されている。もし、これをどうしても型定義で行いたいのであれば、このような感じになるだろうか。

type Opacity = '0' | `0.${number}` | '1'
// OK
const opacity05: Opacity = '0.5'
const opacity1: Opacity = '1'
const opacity0: Opacity = '0'
// NG
const opacityError: Opacity = '10'

ただし、これだと、文字列として取り扱うことになるので、困る場合もありそうだ。即席だが、数値として、代入するために、ジェネリックスを代用できればどうだろうか。

type OpacityLiteral = '0' | `0.${number}` | '1'
type Opacity<T extends number> = `${T}` extends OpacityLiteral ? T : never
// OK
const opacity05: Opacity<0.5> = 0.5
const opacity1: Opacity<1> = 1
const opacity0: Opacity<0> = 0
// NG
const opacityError: Opacity<10> = 10

定数となるため、変数に再代入する場合には、あまり使えないが、悪くないと思う。しかし、文字列で扱ったほうが明らかに簡単そうで、数値だと、検証用の関数を通す必要が出そうなので、あまり複雑にしすぎるのも考えものだ。
後日、少し考えてみるが、なにかアイデアがあれば、コメントいただければ嬉しい。

Discussion