TypeScript 演習
この章では教材の「TypeScript」の内容を扱っています。
問題 1
以下の 2 つの TypeScript のコードについて、エラーが表示される行を全て選んでください。
問題 1-1
let a: unknown = "a";
let b: string = "a";
let c: "a" = "a";
b = a;
b = c;
console.log(a.length);
console.log(b.length);
console.log(c.length);
解答
正解: 4 行目、6 行目
- 1~3 行目は、文字列
"a"
はunknown
型、string
型、"a"
型のいずれにも含まれるのでエラーは出ません。 - 4 行目は、
unknown
型の変数をstring
型に代入しようとしていますが、unknown
型はstring
型の部分集合ではないのでエラーが出ます。 - 5 行目は、
"a"
型はstring
型の部分集合なのでエラーは出ません。 - 6 行目は、
unknown
型はlength
プロパティを持たないためエラーが出ます。 - 7 行目は、
string
型はlength
プロパティを持つためエラーは出ません。 - 8 行目は、
"a"
型はstring
型の部分集合なのでlength
プロパティを持ち、エラーは出ません。
問題 1-2
type T = { x: number };
type U = { x: number; y: number };
let a: T = { x: 1 };
let b: T = { x: 1, y: 2 };
let c: U = { x: 1 };
let d: U = { x: 1, y: 2 };
a = d;
console.log(a.y);
console.log(d.y);
解答
正解: 4 行目、5 行目、8 行目
- 4 行目のようにプロパティを直接記述する場合は、余分なプロパティが含まれているとエラーが出ます。7 行目との違いに注意してください。
- 5 行目は、必要なプロパティが不足しているためエラーが出ます。
- 7 行目は、
U
型はT
型の部分集合であるためエラーは出ません。 - 8 行目は、7 行目で
a
にd
を代入しているため問題なさそうに見えますが、a
はT
型だと宣言されているため、y
プロパティを持たないという扱いになり、エラーが出ます。このように、余分なプロパティを持つオブジェクトを代入できても、余分なプロパティにアクセスすることはできません。 - 9 行目は、
U
型はy
プロパティを持つためエラーは出ません。
問題 2
TypeScript を用いて、以下の要件を満たす関数 welcome
を作成してください。
- 引数として
User
型のオブジェクトを受け取る。User
型は、下にあるコードの通りに定義する。ここで、type
プロパティはユーザーの種類 (ゲスト、一般会員、プレミアム会員)、name
プロパティはユーザーの名前、daysLeft
プロパティはプレミアム会員の有効期間である。 type
プロパティの値に応じて、以下の内容をコンソールに表示する(xxx
の部分には適切な情報を当てはめる)。type
が"guest"
ならば、ようこそ ゲストさん
type
が"general"
ならば、ようこそ xxxさん
type
が"premium"
ならば、ようこそ xxxさん プレミアム会員の有効期間は残りxxx日です
type Guest = { type: "guest" };
type General = { type: "general"; name: string };
type Premium = { type: "premium"; name: string; daysLeft: number };
type User = Guest | General | Premium;
function welcome(user: User) {
// ここに関数の内容を記述
}
// 使用例
welcome({ type: "guest" }); // ようこそ ゲストさん
welcome({ type: "general", name: "太郎" }); // ようこそ 太郎さん
welcome({ type: "premium", name: "太郎", daysLeft: 30 }); // ようこそ 太郎さん プレミアム会員の有効期間は残り30日です
解答
このように、共通のプロパティ (ここでは type
) を用いて複数の型を判別できるようにした型はタグ付きユニオンなどと呼ばれます。TypeScript ではタグ付きユニオンへのサポートが手厚く、条件分岐を行うと自動で型が絞り込まれます。
type Guest = { type: "guest" };
type General = { type: "general"; name: string };
type Premium = { type: "premium"; name: string; daysLeft: number };
type User = Guest | General | Premium;
function welcome(user: User) {
if (user.type === "guest") {
// この中では user は Guest 型として扱われる
console.log(`ようこそ ゲストさん`);
} else if (user.type === "general") {
// この中では user は General 型として扱われる
console.log(`ようこそ ${user.name}さん`);
} else {
// この中では user は Premium 型として扱われる
console.log(
`ようこそ ${user.name}さん プレミアム会員の有効期間は残り${user.daysLeft}日です`
);
}
}
問題 3
次の関数 count
は、配列 a
の各要素を引数として関数 f
を実行し、戻り値が true
となる要素の個数を返す関数です。ただし、f
の戻り値は論理値 (true
または false
) とします。ジェネリクスを用いて適切な型をつけてください。
function count(a, f) {
let result = 0;
for (const x of a) {
if (f(x)) result += 1;
}
return result;
}
// 使用例
console.log(count([1, 2, 3], (x) => x >= 2)); // 2
console.log(count(["a", "aa"], (x) => x.length === 2)); // 1
解答
論理値は boolean
型です。
戻り値の型は推論されるため、省略しても構いません。
function count<T>(a: T[], f: (x: T) => boolean): number {
let result = 0;
for (const x of a) {
if (f(x)) result += 1;
}
return result;
}