TSKaigi Kansai 2024 で配布したTypeScript試験問題「型情報 I」を公開します

Helpfeelエンジニアの id:nedew です。

関西の TypeScript 関係者が集う一大イベント TSKaigi Kansai 2024 が11月16日に京都で開催され、弊社HelpfeelもGold Sponsorとして参加しました。

弊社ブースでは恒例のガチャガチャやステッカーなどの配布に加え、今回の TSKaigi Kansai 2024 用に制作した TypeScript の試験問題を配布を行い、たくさんの方々に解答していただきました!

今回はそんなTypeScript試験「型情報 I」の問題および解答を公開します。

実際に会場で配布した問題と回答用紙のデータも公開しておりますので、より試験っぽい雰囲気を味わいたい方は以下からご覧ください!

gyazo.com

型情報Iの試験問題用紙の表紙

問題解説

問1

以下の TypeScript に関する問題に答えなさい。 (配点 50)

〔1〕 TypeScript のunknown型について、正しい説明を選びなさい。

A. unknown 型は any 型と同じで、どんな操作でも型エラーにならない。 
B. unknown 型の変数を利⽤する際には、事前にその型を判別するための型の絞り込みが必須である。 
C. unknown 型は他の型に直接代⼊できる。 
D. unknown 型の利⽤は strict モードでは許可されない。 

解答と解説を見る
解答: B

unknown型 は TypeScript 3.0 で導入された型で、any型に似て「どんな型でも代入できる」という特徴がありますが、unknown型をそのまま利用することはできません。 unknown型の変数を利用する場合、まずは「その変数がどの型に該当するか」を判別し、適切な型の絞り込みを行う必要があります。

const value: unknown = "Hello, TypeScript!";
// value が文字列型か確認
if (typeof value === "string") {
  // このブロック内において value は string 型の変数として扱えます
  const message: string = value
}
  • 他の選択肢について
    • A: unknown型は any型とは異なり、直接的な操作を行うと型エラーが発生するため、any型のようにどんな操作も許容されるわけではありません。
    • C: unknown型は他の型に直接代入できません。型の絞り込みやキャストを行う必要があります。
    • D: unknown型は strict モードでも利用可能で、むしろ strict な型付けに役立つツールとして使われます。

〔2〕 NonNullable<Type>によって取り除かれる型をすべて選びなさい。

A. null
B. undefined
C. void
D. never

解答と解説を見る
解答: A, B

TypeScript のユーティリティ型NonNullable<T>は、ユニオン型から null および undefined を取り除く型を返します

type Example = NonNullable<string | null | undefined>; // 結果: string
  • その他の選択肢
    • Cのvoidは関数が値を返さないことを示す型で、NonNullable<T>によって取り除かれません
    • Dのneverは「値を持たない」を示す型で、こちらもNonNullable<T>によって取り除かれません

www.typescriptlang.org

〔3〕 次の条件型はどの型と等しくなりますか。

type Result = 1 extends number ? "Yes"   : "No";
A. 1
B. number
C. "Yes"
D. "No"

解答と解説を見る
解答: C

この問題では、条件型を利用した型の評価について問われています。

TypeScript の条件型は以下の形式で記述されます。

A extends B ? X : Y

これは A が B に代入可能であれば X になり、そうでない場合は Y になります。

これらを踏まえて問題を見返すと、number型は1というリテラル型を内包する super type であり、1number型に代入可能なため、true branch側の"Yes"が正解です。

〔4〕 次のResult型に⼀致する型を選びなさい。

type MysteryType<T> =   T extends Array<infer U> ? U : T;
type  Result = MysteryType<string[]>;
A. string
B. string[]
C. boolean
D. never

解答と解説を見る
解答: A

この問題では、条件型(Conditional Types)と infer を利用した型推論の仕組みについて問われています。

  • T extends Array<infer U> ? U : TT が配列型に代入可能であれば U を返し、そうでなければ T を返します。
  • infer U型推論を行うキーワードで、T が配列型の場合、その配列の要素型を推論して U に割り当てます
  • この問題ではstring[]が渡されているため、Tstring型 の配列であり、Ustring型であると推論されます
    • (つまりnumber[] を渡した場合は Unumberとなる)
  • よってTstring[]のときT extends Array<infer U>の左辺は右辺に代入可能となり、string と推論された U が返るため、正解はAの string です

〔5〕 以下の単語について、TypeScript 5.6 までにキーワードとして解釈できるようになった順に正し く並べられたものをひとつ選びなさい。

A. readonly -> protected -> using -> satisfies
B. satisfies -> using -> protected -> readonly
C. using -> readonly -> satisfies -> protected
D. protected -> readonly -> satisfies -> using

解答と解説を見る
解答: D

それぞれ下記のバージョンにてキーワードとして解釈可能になりました。

問2

Products型には、株式会社 Helpfeel のプロダクト名が列挙され、各プロダクトの提供状況が⽂字列で記載されています。
この中から「提供中」のプロダクト名だけを抽出し、ユニオン型として表現するAvailableProductName型を完成させてください。 (配点 20)

type Products = {
  Gyazo: '提供中';
  Helpfeel: '提供中';
  Scrapbox: 'ScrapboxはCosenseに名前が変わりました';
  Cosense: '提供中';  
};

// AvailableProductNameが "Gyazo" | "Helpfeel" | "Cosense" になるように完成させてください
type AvailableProductName = { [K in keyof Products]: Products[K] extends '提供中' ? 【解答】 : never }[keyof Products];
A. string
B. K
C. Products
D. Products[K]

解答と解説を見る
解答: B

Mapped Typesへの理解と、neverを用いたユニオン型のフィルタリング知識を問う問題です。

  • [K in keyof Products] によって、Products型のすべてのキー("Gyazo" | "Helpfeel" | "Scrapbox" | "Cosense")が K に渡されます
  • 「【解答】」の箇所に解答Bの K を当てはめて Products[K] extends '提供中' ? K : never とします
    • キー K に対応する値 Products[K] を条件型で評価します
    • 値が '提供中' であればキー K を結果に含め、それ以外の場合は never を返します
  • これらのステップを終えると以下のような型が得られているでしょう
{
  Gyazo: "Gyazo";
  Helpfeel: "Helpfeel";
  Scrapbox: never;
  Cosense: "Cosense";
}
  • 上記ステップで得られた型から[keyof Products]を用いて全ての値の型を抽出します
  • 抽出した値はユニオン型になりますが、neverはユニオン型から除外されるため、最終的に以下の期待する型が得られます
"Gyazo" | "Helpfeel" | "Cosense"

問3

株式会社 Helpfeel は 3 つのプロダクトを展開しています。それぞれのアイコンを答えなさい。(配点 20)

  1. Gyazo
  2. Cosense
  3. Helpfeel

株式会社Helpfeelが提供する3プロダクトのロゴ画像が載っています

① 1 ‒ A、2 ‒ C、3 ‒ B
② 1 ‒ C、2 ‒ B、3 ‒ A
③ 1 ‒ B、2 ‒ A、3 ‒ C

解答と解説を見る
解答: ③

よく見るとそれぞれプロダクト名の頭文字を反映させたロゴになっています。
ブースで配布していたチラシやパンフレットにもヒントがあったようです。

問4

次のコードを実⾏したとき、コンソールに出⼒される⽂字列はどれですか。 (配点 10)

function getRecruitmentMessage(): string {
  if ("true" === "true") {
    return "Helpfeel社はエンジニア、マネージャーなど幅広く大募集中!あなたの出番です!";
  } else {
    return "Helpfeel社は現在募集を行っておりません";
  }
}
console.log(getRecruitmentMessage());
①Helpfeel社はエンジニア、マネージャーなど幅広く大募集中!あなたの出番です!
②Helpfeel社は現在募集を行っておりません

解答と解説を見る
解答: ①

株式会社Helpfeelはエンジニアやマネージャーなど積極採用中です。
カジュアル面談へのご応募をお待ちしております!

recruit-engineer.helpfeel.com

まとめ

皆さんは何点でしたか?

こういった試験問題の配布は初の試みだったのでうまくいくか不安でしたが、予想以上に多くの方に解答していただき、また楽しんでいただけたのかなという感触があり、やってよかったなという気持ちです。

一方で問題の難易度や採点フローなど課題も見えたので、また機会があればリベンジさせてください。

改めてブースに足を運んでいただいた皆さん本当にありがとうございました。
次回の undefined 試験でお会いしましょう。