Typescript プリミティブな値のストリングリテラル型を作ってみる
Typescript プリミティブな値のストリングリテラル型を作ってみる
背景
TypeScript では、プリミティブな値の型として string
number
boolean
などがあり、
"hoge"
3
false
などを型で区別することができるが
では、これらが文字列の場合("hoge"
"3"
"false"
)は型システムを使って区別をつけることができるのかと疑問に思い、型が作れないか試してみることにした。
プリミティブな値とは
サバイバルTypescriptでも解説があるが、基本的にプリミティブ型とは以下の通り
boolean
number
string
undefined
null
symbol
bigint
他の型
その他、プリミティブではないが、TypeScript には以下の型もあるので、ついでに試してみる。
object
any
unknown
never
プリミティブな値の文字列型
Number
例えば数値の文字列型を定義するなら、以下のように書く
type NumberString = `${number}`
この型をいろんな文字列に当てはめてみると・・
const num1: NumberString = "123" // ok
const num2: NumberString = "-123.456" // ok
const num3: NumberString = "0x3d4f" // ok
const num4: NumberString = "0b1011" // ok
const num5: NumberString = "0o237" // ok
const num6: NumberString = "123e-1" // ok
const numx1: NumberString = "abc" // Type '"abc"' is not assignable to type '`${number}`'.(2322)
const numx2: NumberString = "NaN" // 同様のエラー
const numx3: NumberString = "Infinity" // 同上
const numx4: NumberString = "0x12fg" // 同上
const numx5: NumberString = "0b1234" // 同上
"NaN"
と "Infinity"
は予想外だったが、基本的に数値にパース可能な文字列全般を示す型として定義できた。
Boolean, null, undefined
Numberの場合と同様に書いてみると、 "true" | "false"
のようなストリングリテラルに変換された
type BooleanString = `${boolean}` // "true" | "false"
ちなみに、同様に true
false
それぞれの型も同様の結果となった
type TrueString = `${true}` // "true"
type FalseString = `${false}` // "false"
null
undefined
についても同様の結果になった
type NullString = `${null}` // "null"
type UndefinedString = `${undefined}` // "undefined"
おそらく、これらは実際に値が文字列化したときの結果が型となってるのではないかと思われる
const trueStr = `${true}` // "true"
const nullStr = `${null}` // "null"
const undefinedStr = `${undefined}` // "undefined"
BigInt
同様の型を作ってみた
type BigIntString = `${bigint}`
いろいろ試してみると、以下のようになった
const bigint1: BigIntString = "123" // ok
const bigint2: BigIntString = "123.456" // error
const bigint3: BigIntString = "-123" // ok
const bigint4: BigIntString = "-123.456" // error
const bigint5: BigIntString = "0x3d4f" // ok
const bigint6: BigIntString = "0b1011" // ok
const bigint7: BigIntString = "0o237" // ok
const bigint8: BigIntString = "123e-1" // error
const bigint9: BigIntString = "456e+10" // error
const bigint10: BigIntString = "123n" // error
見たところ、 整数の文字列がほぼ通っているようだった。
しかし、 456e+10
, 123n
がエラーとなるのは腑に落ちない。 (TypeScript側の不具合?)
不思議な挙動はあれど、整数のみを受け付ける文字列型ができた。これは応用性ありそう
symbol, object
予想はしていたが、これらの型はstringでの表現が不可能であり、以下のように型を作っても、当然ながらエラーとなった
type SymbolString = `${symbol}` // Type 'symbol' is not assignable to type 'string | number | bigint | boolean | null | undefined'
type ObjectString = `${object}` // Type 'object' is not assignable to type 'string | number | bigint | boolean | null | undefined'
string にパース可能な primitive type は 上記のエラーの文言の通り決まっており、これ以外の型ではダメなようだ。
any, unknown, never
せっかくなので試してみた。
type AnyString = `${any}` // `${any}`
type UnknownString = `${unknown}` // Type 'unknown' is not assignable to type 'string | number | bigint | boolean | null | undefined'
type NeverString = `${never}` // never
any については `${any}` 型となった。が、これって実質 string
だよね・・
unknown は string にパース不可能なためエラーとなった
never については、予想通り never 型になった。
まとめ
いろいろ試してみたが、BigIntString は予想外だった。
これらについては以下の応用が聞くかも
応用例
何らかのAPIやモジュールで、string number, number が不一致な状態を正すメソッドを作るとか
type NumberString = `${number}`
type NumberParsable = number | Number | NumberString
declare function parseNumber(likeNumber: NumberParsable): number;
Discussion