型システム

2.DataWeave の x バージョンでは型システムがサポートされます。型システムで実行される型チェックを利用するには、このセクションで説明されている変数および関数の制約式を提供する必要があります。この型の値を定義する方法についての詳細は、​「型の値構成」​を参照してください。

型システムでは、次のような構成要素のセットに対して一連の制約を定義します。

  • 変数

  • 関数パラメーター

この制約は、型チェックフェーズで、関数コールの変数または引数に割り当てられた値に DataWeave で制約を確実に適用する場合に使用されます。次に例を示します。

DataWeave スクリプト
%dw 2.0
import * from dw::core::Strings
output application/json

var userName: String = "John"

userName​ の変数定義には、型 ​String​ の値を割り当てるといった制約があります。 他の値のデータ型が割り当てられた場合、型チェックエラーが発生します。

DataWeave スクリプト
%dw 2.0
import * from dw::core::Strings
output application/json

fun toUser(id: Number, userName: String): String = "you called the function toUser!"

関数 ​toUser​ のコールに対する制約では、1 番目の引数の型が ​Number​、2 番目の引数の型が ​String​ である (これらの型に強制変換できるようにする) 必要があります。また、別の制約では、関数の結果の型が ​String​ である必要があります。この制約は ​toUser​ のコールでは使用されません。これは、関数本文で適切な型が生成されたことを検証するための定義に適用されます。

toUser(123, "John")
toUser("123", true)

2 番目のコールは失敗することが予想される場合がありますが、自動型強制を使用するとコールを正常終了し、両方のコールを正常に機能させることができます。2 番目の関数コールの場合は、​String​ 値 ​"123"​ を ​Number​ 値 ​123​ に変換し、​Boolean​ 値 ​true​ を ​String​ 値 ​"true"​ に変換する自動型強制があり、これにより関数をコールすることができます。
型チェックエラーが発生するコールの例として、​toUser("a 12", "John")​ があります。String (文字列) 値 ​"a 12"​ を ​Number​ 型に型強制できないためです。

また、DataWeave は、制約として型が指定されていない場合でも、グローバル型推定アルゴリズムを使用してコードを検証します。

制約の使用は省略可能ですが、大きなスクリプトや複数のスクリプトでは制約が役立つ場合があります。型システムでは型チェックアルゴリズムが実行されるため、DataWeave ロジックのバグを回避するには型システムが役立ちます。また、IDE でスクリプトを記述してこの制約を定義する場合、このツールを使用して型チェックエラーを見つけることができるため、スクリプトが実行されたときにスクリプトが失敗することはありません。

DataWeave の型システムでサポートされる型は 3 つのカテゴリに分割されます。

  • 単純型

  • 複合型

  • 複雑型

単純型

単純型は文字列やブールなどの値を表します。この値はアトミックです。つまり、この値は他の値から構成されていません。次の型は単純型です。

  • String

  • Boolean

  • Number

  • Regex

  • Null

  • 一時的: Date​、​DateTime​、​LocalDateTime​、​LocalTime​、​Time​、​Period

Null

Null​ は 1 つの値 (​null​) のみの型です。つまり、​null​ を、型 ​Null​ を除き、型 ​String​ やその他の型に割り当てることはできません。

たとえば、String Module 内の ​repeat​ 関数にシグネチャー ​repeat(String, Number): String​ があるとします。つまり、この関数は 1 番目のパラメーターとして型 ​String​ の値のみを受け入れ、2 番目のパラメーターとして型 ​Number.​ の値のみを受け入れます。この関数は型 ​String​ の値を返します。

DataWeave スクリプト
%dw 2.0
import * from dw::core::Strings
output application/json
---
repeat("a", 3)

これは ​"aaa"​ を返しますが、次の例ではエラーがスローされます。値 ​null​ を、型 ​String​ の値が想定されるパラメーターに割り当てることはできないためです。

DataWeave スクリプト
%dw 2.0
import * from dw::core::Strings
output application/json
---
repeat(null, 3)

次の例では、スクリプトは 1 番目のパラメーターに型 ​Null​ の値を割り当てます (この値の型は ​Type<Null>​)。自動型強制によって値が型 ​String​ (​"Null"​) に変換されるため、これは ​"NullNullNull"​ を返します。

DataWeave スクリプト
%dw 2.0
import * from dw::core::Strings
output application/json
---
repeat(Null, 3)

一部の関数には、​null​ を使用してコールできるオーバーロード状態のシグネチャーがあります (関数にはさまざまなパラメーター種別の複数の定義があります)。

たとえば、​dw::core::Strings​ からの ​isNumeric​ 関数にシグネチャー ​isNumeric(String): Boolean​ があるとします。これは、文字列を受け取り、その文字列が数値の場合に結果を正常に返しますが、関数は ​isNumeric(Null): Boolean​ としても定義されています。

このため、​null​ (​isNumeric(null)​) を使用して関数 ​isNumeric​ をコールした場合、関数は例外をスローしません。実行時に、ディスパッチアルゴリズムである関数は、使用される値の型に基づいて正しい関数を選択します。​null​ を引数とする ​isNumeric​ の定義のため、この関数は ​false​ を返します。

複合型

複合型には他の値が含まれます。複合型は ​Array​、​Object​、​Function​ です。

Array (配列) 型

配列の型は ​Array<T>​ です。ここで、​T​ は、配列内の要素の型を定義する型パラメーターです。たとえば、​Number​ の ​Array​ 型を使用して変数を定義する構文を次に示します。

var idsList: Array<Number> = [1, 22, 333, 4444]

変数 ​idsList​ は、その変数に割り当てられた配列に ​Number​ 型の値のみが含まれる場合のみ、型チェックフェーズに合格します。

Object (オブジェクト) 型

Object​ 型は ​Object​ または ​{}​ の 2 つの方法で定義できます。この両方でオープンオブジェクトが定義されますが、キー-値ペアに対する制約は指定されません。「​クローズドオブジェクトとオープンオブジェクト​」も参照してください。

キー-値ペアに対する一連の制約を使用して ​Object​ 型を定義する構文は、​Object​ 型の値を定義する構文によく似ています。

{
    keyName @(attrName: AttrType): valueType,
    ...
}

たとえば、​firstNameString​、​lastNameString​、および ​ageNumber​ を使用して ​User​ 型を定義する構文を次に示します。

DataWeave スクリプト
%dw 2.0
output application/json

type User = {
    firstName: String,
    lastName: String,
    age: Number
}

各キー-値ペアに修飾子を割り当てることで、項目が常に存在することや、項目が反復する可能性があることを指定できます。

反復する項目の場合は、​*​ を使用します。たとえば、次の型では、複数の ​lastName​ 項目を持つことができます。

DataWeave スクリプト
%dw 2.0
output application/json

type User = {
    firstName: String,
    lastName*: String,
    age: Number
}

条件付き項目の場合は、​?​ を使用します。たとえば、次の型では、​age​ 項目は存在してもしなくても構いません。

DataWeave スクリプト
%dw 2.0
output application/json

type User = {
    firstName: String,
    lastName*: String,
    age?: Number
}

クローズドオブジェクトとオープンオブジェクト

Object​ 型はクローズドまたはオープンにすることができます。​クローズド​オブジェクトでは、型で指定されたキー-値ペア以外の項目がない場合のみ、オブジェクト値を受け入れます。

オープン​オブジェクトでは、型で宣言された項目に対してのみ制約が適用され、すべての制約が検証された場合に型は値を受け入れます。この型は、宣言で示された項目とは別の項目を含むオブジェクトを受け入れます。特に指定しない限り、すべての ​object​ 型がオープンです。

次の例は、​age​ が型宣言に含まれていなくても、変数 ​user​ の型がオープンオブジェクトのため、成功します。

DataWeave スクリプト
%dw 2.0
output application/json

var user: {firstName: String, lastName: String} = {firstName: "John", lastName: "Smith", age: 34}

クローズドオブジェクトを指定する構文は ​{| |}​ です。次の例では、​User​ 型でサポートされるキーを ​firstName​ と ​lastName​ のみに限定します。​age​ 項目が受け入れられないため、このスクリプトは例外をスローします。

DataWeave スクリプト
%dw 2.0
output application/json

var user: {|firstName: String, lastName: String|} = {name: "John", lastName: "Smith", age: 34}

Function (関数) 型

DataWeave は関数型言語であり、関数は第一級オブジェクトとみなされます。つまり、関数は、関連付けられた型を持つ値です。

関数型を定義する構文を次に示します。

(paramType: ParamType,...) -> ReturnType

たとえば、型 ​String​ のパラメーター、型 ​Number​ の 2 番目のパラメーター、および ​Boolean​ 戻り値を持つ ​Function​ 型を制約で定義する場合、正しい構文は次のようになります。

(paramA: String, paramB: Number) -> Boolean

次の例では、別の関数を引数として受け取る関数を定義します。

DataWeave スクリプト
%dw 2.0
output application/json

fun applyIDsChange(ids: Array<Number>, changeTo: (Number) -> Number): Array<Number> = ???

changeTo​ の制約 ​(Number) → Number​ に一致しない関数を含む関数 ​applyIDsChange​ をコールすると、DataWeave は型チェックエラーをスローします。

たとえば、次のコールでは、​abs​ が ​Number​ 型の値を取り、​changeTo​ パラメーターに一致する ​Number​ を返す関数のため、コールは正常に機能します。

applyIDsChange([1,-6, 3, -8], abs)

ただし、次のコールでは、​sum​ が ​Array​ を取り、パラメーター制約に一致しない ​Number​ を返す関数のため、コールは失敗します。

applyIDsChange([1,-6, 3, -8], sum)

複雑型

複雑型には、​Any​ と ​Nothing​ 型、​Union​ 型、​Intersection​ 型、および ​Literal​ 型が含まれます。この各型の名前は直感的なわかりやすい名前です。

Any (任意) と Nothing (空)

場合によって、型がすべての値を受け入れるために制約を適用できないことがあります。

  • Any​ 型は、可能なすべての値を受け入れます。

  • Nothing​ 型は値を受け入れませんが、すべての型に割り当てることができます。この型は明示的に使用されることは頻繁にはありませんが、型推定アルゴリズムで使用されます。

Union (結合) 型

Union​ 型は、型を構成するために使用されます。​Union​ 型を定義する構文を次に示します。

TypeA | TypeB | ...

次の例では、型 ​String​ または ​Number​ の値を受け入れる制約を使用して変数を定義します。

var age: String | Number = if (payload.allStrings) "32" else 32

一般的なパターンでは、​Null​ 型を ​Union​ 型と共に使用して、型が ​null​ を値として受け入れることを指定します。次の例では、関数 ​parseEmail​ は型 ​String​ または ​Null​ の入力を許容します。この場合、ペイロードの省略可能な項目 ​email​ を含むオブジェクトを使用でき、​payload.email​ を使用して関数をコールできます。

fun parseEmail(email: String | Null) = "Code that handles email being of type String or Null"

Intersection (交差) 型

DataWeave 2.3.0 で導入されました。Mule 4.3 以降でサポートされます。

Intersection​ 型は ​Object​ 型を交差させます。この場合、交差はオブジェクト型の連結 (​++​) として機能します。

Intersection​ 型の構文を次に示します。

TypeA & TypeB & ...

次の例では、​Intersection​ が 2 つの ​Object​ 型を連結し、結果として型 ​{name: String, lastName: String}​ が生成されます。この型は、追加のキー-値ペアを受け入れることができるオープンオブジェクトです。変数は、その変数に割り当てられた値を受け入れます。

var a: {name: String} & {lastName: String} = {name: "John", lastName: "Smith", age: 34}

クローズドオブジェクトの場合、オブジェクト型の連結が返されますが、結果としてクローズドオブジェクトが生成されます。
次の例では、交差により型 ​{|name: String, lastName: String|}​ が生成されますが、オブジェクト内に追加項目 (項目 ​age​) がある場合、クローズドオブジェクトは ​Object​ 値を受け入れないため、例外がスローされます。

var a: {|name: String|} & {|lastName: String|} = {name: "John", lastName: "Smith", age: 34}

Literal (リテラル) 型

DataWeave 2.3.0 で導入されました。Mule 4.3 以降でサポートされます。

リテラル型は 1 つの値を表します。たとえば、​String​ 値 ​"foo"​ は型 ​"foo"​ で表すことができます。

次のリテラル型は型システムに含まれます。

  • String​ リテラル型

  • Number​ リテラル型

  • Boolean​ リテラル型

リテラル型を ​Union​ 型と共に使用して、許容される値の有限セットとして型を宣言できます。たとえば、次の型宣言は ​Union​ 型とリテラル型の別名です。

type Weekdays = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday"
type Days = Weekdays | "Saturday" | "Sunday"

次の例では、変数はリテラル型 ​404​ および ​500​ の値のみを受け入れます。型システムでは、実行前に変数を確実にこれらのリテラル値のいずれかのみとすることができます。

DataWeave スクリプト
%dw 2.0
output application/json

var errorStatusCode: 404 | 500 = payload.statusCode match {
    case "error 404" -> 404
    case "error 500" -> 500
}

関数オーバーロードでは、引数の値に基づいて異なる動作を定義できます。

DataWeave スクリプト
%dw 2.0
import * from dw::core::Strings
output application/json

fun errorHandling(errorCode: 404 | 405, response): String = "Code for error 4XX handling here!"
fun errorHandling(errorCode: 500 | 501, response): String = "Code for error 5XX handling here!"
---
errorHandling(payload.statusCode, payload)

実行時に関数ディスパッチアルゴリズムが ​payload.statusCode​ の値の型に基づいて正しい関数を選択します。関数の動作を変更するために ​if​ ステートメントを使用して引数の値を確認する必要はありません。

型パラメーター

型パラメーターを使用すると、関数またはデータ型を汎用的に記述できるため、値の型に依存することなく、値を同様に処理できます。

たとえば、型情報を失うことなく配列の最初の要素を返す関数を定義できます。この例では、​head​ 関数が入力として取る配列内の項目の型を変数定義 ​firstString:String​ と ​firstNumber:Number​ で指定します。

%dw 2.0
fun head<ItemType>(elements: Array<ItemType>): ItemType = elements[0]!

var firstString:String = head(["DataWeave", "Java", "Scala", "Haskell"]) ++ " Rules!!!"
var firstNumber:Number = head([1,2,3])
output application/json
---
firstNumber

スクリプトの本文で、​firstNumber​ は数値 ​1​ を返します。​firstNumber​ を ​firstString​ に置き換えると、スクリプトは文字列 ​"DataWeave Rules!!!"​ を返します。

型パラメーターのバインド

型パラメーターをバインドして、特定の型の値を要求できます。次の例での ​{name: "DataWeave"}​ の追加は有効です。これは、関数定義内の ​{name: String}​ で、​name​ キーの値は文字列である必要があることが示されているためです。

%dw 2.0

fun addName<User <: {} >(user: User): User & {name: String} = user ++ {name: "DataWeave"}

var myUser: {name: String, developers: Number} = addName({developers: 3})

型定義のパラメーター化

型パラメーターを使用して、型定義をパラメーター化できます。たとえば、1 つはテキストファイル、もう 1 つはバイナリファイルのファイルのコンテンツをモデル化するデータ構造を定義するとします。型パラメーターを使用して、正しい実装をディスパッチする新しい型を作成できます。

%dw 2.0

type FileData<Content> = {
    data: Content,
    name: String
}

fun read(file: FileData<String>) =  "This is a text file with data " ++ file.data
fun read(file: FileData<Binary>) =  "This is a binary file with data " ++ (file.data as String {base: "64"})

---
{
  binary: read({data: "Hello World" as Binary, name: "myFile.bin"}),
  text: read({data: "Hello World", name: "myFile.txt"})
}