型パズルとは
型パズルの例
以下に、TypeScript の型パズルの例を 3 つ紹介します。
① 関数の引数と戻り値の型を指定し、指定された仕様を満たすプログラムを作成する問題。例えば、「引数として 2 つの整数を受け取り、その 2 つの整数を足した値を返す関数を作成しなさい」。
① 型エイリアスを使用して、複雑な型を定義し、その型を指定された仕様に沿って操作する問題。例えば、「型エイリアスを使用して、配列内の各要素が数値であることを表す型 ArrayOfNumbers を定義しなさい。そして、ArrayOfNumbers 型の値を受け取り、各要素の合計値を返す関数を作成しなさい」。
③ ジェネリックを使用して、汎用的な型を定義し、その型を指定された仕様に沿って操作する問題。例えば、「ジェネリックを使用して、配列内の各要素が同じ型であることを表す型 SameTypeArray<T> を定義しなさい。そして、 SameTypeArray<T> 型の値を受け取り、各要素の合計値を返す関数を作成しなさい」。
解答
①
function sum(x: number, y: number): number {
return x + y;
}
②
type ArrayOfNumbers = number[];
function sumArray(numbers: ArrayOfNumbers): number {
let sum = 0;
for (const num of numbers) {
sum += num;
}
return sum;
}
③
type SameTypeArray<T> = T[];
function sumArray(numbers: SameTypeArray<number>): number {
let sum = 0;
for (const num of numbers) {
sum += num;
}
return sum;
}
型パズルの非 直感的な挙動
型パズルがパズルとして成り立つのは、言ってしまえば、型システムが人間の直感とことなるからです。 仮に、素直におもいついた型定義が、そのままtypescriptのコンパイラにとって解釈可能で正しいものであれば、パズルとし機能しないことを考えれば逆説的に明らかです
TypeScript の型システムは、型推論に基づいて型を推論することができます。型推論とは、プログラム中における値の型から、変数や関数の型を推測することです。これは、プログラマが型を明示的に定義する必要がなくなるため、プログラミングが楽になるという利点があります。 しかし、人間が直感的に想像する型推論と、TypeScript が行う型推論とは、必ずしも一致しない場合があります。そのため、TypeScript の型パズルを解く際には、型推論が行われる順序や方法を理解することが重要です。 以下に、人間が直感的に想像する型推論と、TypeScript が行う型推論が異なる例を紹介します。
let x: number | string;
x = "hello";
x = x.concat(" world");
console.log(x.length);
この問題では、変数 x の型が number | string と定義されています。まず、文字列の値 "hello" を代入しているため、変数 x の型は string と推定されます。次に、concat メソッドを使って文字列 " world" を連結しているため、変数 x の型は、連結した結果として string 型の値が得られます。
しかし、次の行では、変数 x の length プロパティを出力しています。このプロパティは、文字列型の値にしか存在しないため、型推論により、変数 x の型は string と推定されます。しかし、人間がこの問題を読む際に は、変数 x の型が string と推定されることは直感的ではありません。
このように、TypeScript の型推論が人間にとって直感的でない場合があります。そのため、TypeScript を使ったプログラミングでは、型宣言を適切に使い、型推論の挙動を理解しておくことが重要です。