何問解ける?TypeScriptクイズ! #techtekt Q

トップ画像 みなさまこんにちは。techtekt編集部です。 パーソルキャリアのエンジニアやデータサイエンティストなどが出題する「techtekt Q」という新たなコンテンツをスタートしました。 社内でよく使われる言語から、注目しているフレームワーク、答えを導くプロセスなど、さまざまな視点でクイズを出題します!

トップバッターをかざってくれたのは、主に新規サービスの開発を担う「サービス開発部」からYuto SAGAWAさんと、@_k725さんからの出題です。さて、あなたは何問解けますか???
※Yuto SAGAWAは退職していますが、本人の同意を得て掲載を継続しています。

出題背景

私達のチームではメインの開発言語にTypeScriptが採用されています。 TypeScriptには静的型付けなどがあり、またトランスパイルする時点でエラーが分かることから、複数名での開発で便利なことが特徴です。 便利である一方ECMAScriptの仕様やTypeScript自体の複雑な仕様、型パズルなど難しい部分も存在します。 そんなTypeScriptから知っていると開発に役立つクイズを出題したいと思います。

[Q1] Omit

interface PremiumPlan {
  users: number;
  generate: boolean;
  support: boolean;
  term: string;
}
type StandardPlan = Omit<PremiumPlan, "support" | "generate">
  1. エラーにならないのはどれ?
// 1
const standardPlan: StandardPlan = {
  users: 100,
  generate: true,
  support: false,
  term: "2021-12-31 23:59:59"
}
// 2
const standardPlan: StandardPlan = {
  users: 100,
  term: "2021-12-31 23:59:59"
}
// 3
const standardPlan: StandardPlan = {
  generate: true,
  support: false
}

解答A. 2
Omitは一部のプロパティを除いた新たな型を構築することができます。 問題ではPremiumPlanからsupportおよびgenerateを覗いた型定義となっています。 参考 https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys

[Q2] Record

type UserCode = string;
type User = {
  name: string;
  age: number;
}
type Users = Record<UserCode, User>
  1. エラーにならないのはどれ?
// 1
const users: Users = {
  0: {name: "John", age: 22},
  1: {name: "Caroline", age: 23}
}
// 2
const users: Users = {
  "A123": {name: "John", age: 22},
  "A124": {name: "Caroline", age: 23}
}
// 3
const users: Users = [
  {UserCode: "A123", name: "John", age: 22},
  {UserCode: "A124", name: "Caroline", age: 23}
]

解答A. 2
Recordはプロパティのキーがオブジェクトのkeyとなり、プロパティの値がオブジェクトのvalueとなります。 ある型のプロパティを別の型にマッピングするために使用されます。 参考 https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type

[Q3] interface

interface User {
  invitationCode: string;
}
interface User {
  name: string;
  age: number;
  email: string;
}
  1. 正しいのはどれ?
// 1
const user: User = {
  name: "John",
  age: 22,
  email: "email@example.com",
  invitationCode: "ABC123"
}
// 2
const user: User = {
  name: "John",
  age: 22,
  email: "email@example.com"
}
// 3
const user: User = {
  invitationCode: "ABC123"
}

解答A. 1
interfaceは同じ名前であれば拡張されます。 最終的にUserは下記のようになります。

interface User {
  name: string;
  age: number;
  email: string;
  invitationCode: string;
}

1ファイルにinterfaceと長い処理が書かれている場合などでは、知らないうちに拡張されていることも考えられます。 interfaceは適宜 models などディレクトリを区切って切り出すことを検討しましょう。

[Q4] keyof | typeof

const commands = {
  keyUp: "KeyUp",
  keyDown: "KeyDown"
}
type Command = ?
const command: Command = "keyUp"; // 条件1: OK
const command: Command = "KeyUp"; // 条件2: Error
const command: Command = "KeyRight"; // 条件3: Error
  1. 上記の条件をすべて満たす Commandの適切な型定義はどれ?
// 1
type Command = keyof commands;
// 2
type Command = typeof commands;
// 3
type Command = keyof typeof commands;

解答A. 3
オブジェクトからキーの型を生成したい場合に使用されます。 typeof でオブジェクトから型を生成します。生成された型に対して keyof を使うとそのオブジェクトのキーをユニオン型として返されます。

type Command = "keyUp" | "keyDown";

[Q5] NaN

console.log(NaN === NaN);
  1. 出力結果は?
// 1
true
// 2
false
// 3
NaN

解答A. 2
ECMAScriptとして定められた挙動です。 x == y の時、以下のように定義されています。

  1. If Type(x) is Number, then
    1. If x is NaN, return false. II. If y is NaN, return false. 参考 https://262.ecma-international.org/5.1/#sec-11.9.3

[Q6] async/await

const kirakiraAsync = (val: string): Promise<string> => new Promise((resolve) => resolve(`✨️ ${val} ✨️`));
console.log(["リンゴ", "バナナ", "ぶどう"].map(async (val): Promise<string> => await kirakiraAsync(val)));
  1. 出力結果は?
// 1
[
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> }
]
// 2
[
  Promise { '✨️ リンゴ ✨️' },
  Promise { '✨️ バナナ ✨️' },
  Promise { '✨️ ぶどう ✨️' }
]
// 3
[
  '✨️ リンゴ ✨️',
  '✨️ バナナ ✨️',
  '✨️ ぶどう ✨️'
]

解答A. 1
Array.prototype.map に渡す関数は同期的に処理されるため、 await を行っても、実際に返ってくる値は待機状態(pending)になります。 3 のような結果を望む場合、以下のように記述することで実現可能です。

console.log(Promise.all(["リンゴ", "バナナ", "ぶどう"].map((val): Promise<string> => kirakiraAsync(val))));

(望むケースはほぼ無いと思いますが、)2 のような結果を望む場合、以下のように記述することで実現可能です。

console.log(await ["リンゴ", "バナナ", "ぶどう"].map((val): Promise<string> => kirakiraAsync(val)));

さて、いかがでしたでしょうか。 次回の #techtektQ もお楽しみに~!

Yuto SAGAWA

Yuto SAGAWA

エンジニアリング統括部 サービス開発部 第1グループ エンジニア

大学在学中に飲食店で利用する滞在時間管理システムやお弁当宅配サービスなどの開発に携わる。卒業後はECシステムのパッケージ開発にフロントエンドエンジニアとして従事。現在はパーソルキャリアにてSalariesの開発に携わっている。現在は退職。

@_k725

@_k725

エンジニアリング統括部 サービス開発部 第3グループ エンジニア

お寿司が好きです🍣

※2021年11月現在の情報です。