リスト型
リスト型は type list
または {}
で型名を囲って指定します。
type list
type any
type { any }
type { number }
type { text }
type { { function } }
リストはany型で作成されますが、型を指定する場合は Value.ReplaceType
を使用します。ただし、互換性のチェックは行われませんので、変換できない値でもエラーにはなりません。
let
// 自動的に type list つまり type { any ) の型になります。
MyList = { 1, 2, 3 }
// number型の要素を持つリストが返されます。
Result = Value.ReplaceType(MyList, type { number })
in
Result
しかし、ここで設定された型は、 Value.Type
で確認しても list
としか表示されません。リストの中身の型を確認するには、 Value.ListItem
を使用します。
let
MyList = {1,2,3},
ReplaceType =
Value.ReplaceType(
MyList,
type { number }
),
Result =
Type.ListItem(
Value.Type( ReplaceType )
)
in
Result // numberが返されます。
レコード型
レコード型は型名を []
で囲って指定します。型名を指定することができますが、指定しない場合は any
型になります。
type [ItemCode, Amount = number]
上記は、商品コードと金額の2つのフィールドを持つレコード型を定義しています。
let
// 初期の型は type [Name = any, Age = any]
Person = [Name = "Joe", Age = 50],
NewType = type [FullName = text, Age = number],
Result = Value.ReplaceType(Person, NewType)
in
Result
上記では、最初は自動的に type [Name = any, Age = any]
の型が与えられます。Value.ReplaceType
によって返される [FullName = "Joe", Age = 50]
のレコードの型は type [FullName = text, Age = number]
に変更されます。
新しい型の項目の情報は、項目の位置によって適用されます。したがって、1番目の項目名 "Name" が "FullName" と定義されていたら "FullName" に変更されます。ただし、 あくまで新しい型を割り当てるときにフィールド名には同じものを指定するようにしてください 。マッシュアップエンジンは、値が型に適合しているかチェックする際に項目名によってマッチングしており、予期しない動作が発生する可能性があります。
また、リストの場合と同様に互換性のチェックは行われません。
抽象型
以下のような抽象型を記述することができます。
type [FirstName = text, optional LastName = text, Age = number] // オプション指定
type [Amount = number, ...] // オープン指定
type record // type[...]と同様のオープン指定
type [] // 空のレコード型
テーブル型
これまでの話を踏まえて、テーブルの定義は以下のように表されます。
type table [SomeColumn = text, AnotherColumn = any, YetOneMore]
[]
の中で、行の型を定義しています。行の型は、レコード型から抽象型を除いたものと同様になります。
#table({"Name", "Age"},{{"Joe", 50}})
これは、いかのような型のテーブルを作成します。
type table [Name = any, Age = any]
テーブルの型に主キーを定義できます。
Type.AddTableKey(
table as type,
columns as list,
isPrimary as logical
) as type
let
Source =
#table(
2,
{
{"List1", {1,2,3}},
{"List2", {4,5,6}}
}
),
UpdateType =
Type.AddTableKey(
Value.Type( Source ),
{ "Column1" },
true
),
ReplaceType =
Value.ReplaceType(Source, UpdateType)
in
ReplaceType
標準ライブラリにはキーを追加する Table.AddKey
関数があり、こちらの方が簡潔です。
Table.AddKey(
table as table,
columns as list,
isPrimary as logical
) as table
let
Source =
Table.AddKey(
#table(
2,
{
{"List1", {1,2,3}},
{"List2", {4,5,6}}
}
),
{"Column1"}, true
)
in
Source
テーブルにキーが設定されていない場合、上の図にある"List2"の行の "List" をクリックした場合、以下のように Source{1}[Column2]
という表現になります。
let
Source =
#table(
2,
{
{"List1", {1,2,3}},
{"List2", {4,5,6}}
}
),
Column2 = Source{1}[Column2]
in
Column2
テーブルにキーが指定されている場合は、Table.AddKey
と Type.AddTableKey
のいずれも以下のように List2 = ReplaceType{[Column1="List2"]}[Column2]
となります。
let
Source =
#table(
2,
{
{"List1", {1,2,3}},
{"List2", {4,5,6}}
}
),
UpdateType =
Type.AddTableKey(
Value.Type(Source),
{"Column1"},
true
),
ReplaceType =
Value.ReplaceType(Source, UpdateType),
List2 = ReplaceType{[Column1="List2"]}[Column2]
in
List2
関数型
関数のパラメーターと戻り値に任意で型を指定することができます。ただし、関数型の定義を行う場合は、両方指定する必要があります。
// 関数にはパラメータの型と戻り値の型の指定は任意。
(total) => total * 0.1
// 関数型の指定では、両方を指定しなければならない
type function(total as number) as number
関数で型の指定を省略した場合は、any
が振られます。上記の例では、function(total as any) as any
となります。
Type Context
型名のリテラルは、"type" というキーワードによって Type Context が参照されます。つまり、"type" に続く識別子が型名の場合はその型を参照し、型名出ない場合は通常の識別子参照として解釈されます。
let
ColumnType = type any
in
type table [Col1 = ColumnType]
型名と同じ名前の変数を参照するようなコードでは、どちらが選択されるでしょうか。
let
record = type table [A = text, B = logical]
in
Type.ListItem(type { (record) }) // (1) return table
//Type.ListItem(type { record }) // (2) return record
//Type.ListItem(type { (type record) } // (3) return record
型の値だけを使用する
型の指定を行う場合の説明をしてきましたが、関数自体の定義ではプリミティブな型名だけが使用できます。この場合、"type"は使用せず、カスタムタイプも使用できません。
1 as nullable number // valid syntax
1 as type nullable number // invalid syntax -- number should not be prefixed with type
SomeRecord is record // valid syntax
SomeRecord is [Name = text] // invalid syntax -- can't use a custom type here
(input as table) => ... // valid syntax
(input as [Col1 = any]) => ... // invalid syntax -- can't use a custom type here
(input as SomeType) => ... // invalid syntax -- can't use an expression for the type here
型の比較
型の比較を行ってみます。
type {number} = type list // false
2つの型が等しいか確認するのに、この方法は適切ではありません。等値演算子を使用して型の値を比較しないでください。型の比較には Type.Is
という関数があります。
Type.Is(type {number}, type list) // true
2番めの引数はnull許容プリミティブ型である必要があります。null非許容プリミティブ型を指定してもエラーになりませんが、信頼される結果が得られません。
型のチェック
Power Query は動的に型指定されます。値が特定の型と互換性があるかどうかのすべてのチェックは、実行時に値の型を調べることによって行われます。変数が型を持つという概念はなく、型キャストも関係なく、実際の値の型が重要です。ここで重要なのは、チャイルドタイプは単なる型の主張であるということです。Mはそれらを検証していませんし、マッシュアップエンジンレベルでそれらを強制することもありません。たとえ検証されていなくても、他のコードやホスト・アプリケーションはそれらを信頼し、そこから価値を引き出すことができるのです。