【TypeScript】?の省略と「string | undefined」のユニオン型を一緒にしない
省略
オブジェクト
オブジェクトのプロパティに?
を付けて宣言することで省略可能になります。
type SampleObj = {
foo: string
bar?: string
}
let obj: SampleObj = {
foo: 'sample',
}
console.log(obj);
// {
// "foo": "sample"
// }
obj = {
foo: 'foo',
bar: 'bar',
}
console.log(obj);
// {
// "foo": "foo",
// "bar": "bar"
// }
SampleObj
のbar
に?
がついているので、省略可能となっています。
最初に定義したfoo
だけしかないオブジェクトと、foo
とbar
を代入したオブジェクトは同じSampleObj型になります。
関数
関数のパラメータに?
をつけると引数が省略可能になります。
function foo(bar: number, baz?: string): void {
console.log(`bar: ${bar}, baz: ${baz}`);
}
foo(123); // bar: 123, baz: undefined
foo(123, 'hello'); // bar: 123, baz: hello
あるいは、パラメータ宣言の後に = 'hello'
のようにしてデフォルト値の設定ができます。
関数を実行する時に、引数を指定しなかった場合にデフォルトの値が代入されます。
function foo(bar: number, baz?: string = 'hello'): void {
console.log(`bar: ${bar}, baz: ${baz}`);
}
foo(123); // 123, hello
foo(123, 'world'); // 123, world
undefinedをチェックする
JavaScriptでは存在しないプロパティにアクセスするとundefinedが返却されます。
省略したプロパティや引数の型はstring | undefined
のようにUnion型(ユニオン型)となります。
(stringなのは省略しているのがstring型のため)
?
で省略したプロパティや関数の引数を使用すると、undefinedの可能性があるのでエラーとなってしまいます。
そのため省略したプロパティや引数はundefinedのチェックをして使用する必要があります。
type SampleObj = {
foo: string
bar?: string
}
function func(obj: SampleObj): void {
// const bar = obj.bar.toUpperCase
// Error: Object is possibly 'undefined'.
// toUpperCaseはstringのメソッドなので、undefinedの可能性があるとエラーとなる
// 三項演算子でundefinedかをチェックする
const bar = obj.bar != null ? obj.bar.toUpperCase : '';
// Null合体演算子でも可能
// const bar = obj.bar ?? '';
// Optional Chainingでも可能
// const bar = obj.bar?.toUpperCase
console.log(bar)
}
let obj: SampleObj = {
foo: 'sample',
}
func(obj) // ""
obj = {
foo: 'foo',
bar: 'bar',
}
func(obj) // "BAR"
?で省略したのと「string | undefined」は同じ意味ではない
前述したように?
で省略するとstring | undefined
というようにUnion型に変換されます。
しかし、?
を使用せずに string | undefined
と自分で定義した場合は省略できず、同じ意味にはなりません。
?
を使わない場合は、たとえundefinedが許されていても、プロパティや関数の引数として宣言しないといけないのです。
type SampleObj = {
foo: string
bar?: string | undefined
}
let obj: SampleObj = {
foo: 'sample',
}
// Error
// let obj: SampleObj
// Property 'bar' is missing in type '{ foo: string; }' but required in type 'SampleObj'.
// : 'bar' is declared here.
// 以下のようにundefinedと宣言するとエラーにならない
// let obj: SampleObj = {
// foo: 'sample',
// bar: undefined
// }
function foo(bar: number, baz: string | undefined): void {
console.log(`bar: ${bar}, baz: ${baz}`);
}
foo(123)
// Error
// Expected 2 arguments, but got 1.(2554)
// : An argument for 'baz' was not provided.
// 以下のようにundefinedと宣言するとエラーにならない
// foo(123, undefined)