555
328

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ゆめみAdvent Calendar 2020

Day 7

0歳娘「パパ、型を作る関数はないの?」

Last updated at Posted at 2020-12-06

2015年、とある休日

よめ太郎「あ・・・あんた!」
よめ太郎「こっち来て!!!」

ワイ「ど、どうしたん?」
ワイ「えらい大っきい声出して」

よめ太郎「娘ちゃんが初めて立ったんや!」

娘(0歳)「ヨチ・・・ヨチ・・・」

ワイ「おお・・・!」
ワイ「これはめでたいこっちゃ・・・!」

娘「・・・パパァ・・・」

ワイ「!?」
ワイ「いま、パパ言うたで!?」
ワイ「立ったと同時に言葉も喋れるんか!?」
ワイ「なんて末恐ろしい子や・・・!」

娘「・・・エテ・・・教エテ・・・」

ワイ「な、なんや喋っとるで・・・」
ワイ「なんや、娘ちゃん・・・?」

娘「・・・パパ・・・」
娘「・・・プログラミング・・・教エテ・・・」

ワイ「ファッ!?」
ワイ「Hello, World!でええか・・・?」

よめ太郎「いや、Hello, World!産声で言うてたわ

ワイ「せやった!」
ワイ「お医者さんが思わず・・・」

医者「リアルHello Worldしてどないすんねん」

ワイ「ってツッコんでたもんな」
ワイ「ほな、今日は何を教えてほしいんや?娘ちゃん・・・」

娘「・・・ええと・・・」
娘「実は、こないだ6ヶ月検診で行った病院から依頼を受けて」
娘「とある管理画面のフロントエンド部分を開発してるの」

ワイ「もう実務!?
ワイ「凄いやないか・・・」

よめ太郎「普通に受け入れてるお前が凄いわ」

ワイ「それで、どんな管理画面なんかいな?」

娘「幼児たちの身体測定の結果を記録するための管理画面なの」

ワイ「ふむふむ」

娘「それでね?」
娘「子供たちの身長とか体重、氏名などを記録する必要があるから」
娘「TypeScriptで、こんなを定義したの」

type Child = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

ワイ「なるほどな」

  • 子供は、姓・名・年齢・身長・体重というプロパティを持っている。

ワイ「・・・ということを表しているんやな」
ワイ「ええ感じやないか」
ワイ「それで、いったい何が分からへんのや?」

娘「えっとね」
娘「氏名や年齢を入力して、子供たちの情報を検索する画面があるんだけど」
娘「そこで使う型が上手く書けないの」

ワイ「と言いますと?」

娘「例えば、氏名で検索する場合は」

const condition: Child = {
    lastName: "やまだ",
    firstName: "たかし"
}

娘「↑こんな感じの条件になるんだけど」
娘「型が合わなくて、エラーになっちゃって」

次のプロパティがありません:
age, height, weight

娘「↑こんなエラーが表示されちゃうの」

ワイ「なるほどな」
ワイ「Child型のはずなのに、姓と名しか持ってないから・・・」

年齢身長体重が足りまへんがな!」

ワイ「って怒られとるわけやな」
ワイ「そういう場合は、こんな型を書けばええで」

type PartialChild = {
    lastName?: string
    firstName?: string
    age?: number
    height?: number
    weight?: number
}

ワイ「プロパティ名の後ろに?を付けてやるんや」
ワイ「姓・名・年齢・身長・体重というプロパティを、全て持っててもいいし」
ワイ「一部だけ持っててもいい、そんな型や」
ワイ「ちなみにPartialってのは、部分的って意味や」

const condition: PartialChild = {
    lastName: "やまだ",
    firstName: "たかし"
}

ワイ「↑ほら、PartialChild型にしたらエラーが消えたで」

娘「パパ、そんなことは知ってるの

ワイ「ファッ!?」

娘「そうじゃなくて」

type Child = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

type PartialChild = {
    lastName?: string
    firstName?: string
    age?: number
    height?: number
    weight?: number
}

娘「↑こんな風に」
娘「ほぼ同じ型を2回も書くのが冗長だから」
娘「もっと効率的な書き方はないのか、ってことを聞きたいの」

ワイ「ぐぬぬ・・・」

娘「Child型を元にPartialChild型を作ってくれるような」
娘「そんな、型を作る関数みたいなのはないの?」

ワイ「誠に残念ながら、ございませんねや・・・」

よめ太郎「あるで」

ワイ「ファッ!?」

Partial<Type>

よめ太郎「ほな、コードを書きながら説明していくで」
よめ太郎「Child型を元にPartialChild型を作るには・・・」

type PartialChild = Partial<Child>

よめ太郎「↑こうや」

娘「へえ、PartialってやつにChild型を渡すと」
娘「さっきのPartialChild型と同じものを作って返してくれるんだ!」

よめ太郎「せや」

娘「じゃあ、逆にPartialChild型からChild型を作るようなやり方もあるの?」

よめ太郎「あるで」

Required<Type>

type PartialChild = {
    lastName?: string
    firstName?: string
    age?: number
    height?: number
    weight?: number
}

よめ太郎「↑この型を元に・・・」

type RequiredChild = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

よめ太郎「↑この型を生成したい場合は・・・」

type RequiredChild = Required<PartialChild>

よめ太郎「↑こうや!」

娘「へえ、Requiredっていうやつに、PartialChildを渡すと」
娘「全部のプロパティから?マークを取ってくれるんだね!」

よめ太郎「そんなイメージや」

Readonly<Type>

娘「じゃあママ」
娘「次は・・・プロパティを上書きできないreadonlyな型を作って!」

よめ太郎「ええで」
よめ太郎「つまり・・・」

type Child = {
    lastName: string
    firstName: string
    age: number
    height: number
    weight: number
}

よめ太郎「↑このChild型を元に・・・」

type ReadonlyChild = {
    readonly lastName: string;
    readonly firstName: string;
    readonly age: number;
    readonly height: number;
    readonly weight: number;
}

よめ太郎「↑こんな型を作ればええんやろ?」

娘「そう!」

よめ太郎「その場合は・・・」

type ReadonlyChild = Readonly<Child>

よめ太郎「↑こうや!」

娘「わぁ、便利だね!」

Omit<Type, Keys>

娘「じゃあママ」
娘「Child型から、heightプロパティとweightプロパティだけ削って」

type NameAndAge = {
    lastName: string
    firstName: string
    age: number
}

娘「↑こんな型を作ることもできる?」

よめ太郎「できるで」

type NameAndAge = Omit<Child, "height" | "weight">

よめ太郎「↑こうや!」

娘「わあ、便利!」

ワイ「(いつ便利やねん・・・)」

※筆者注:割と使うときあります。

ReturnType<Type>

娘「じゃあママ」
娘「例えば・・・」

const sameCheck = (a: number, b: number): boolean => a === b

娘「↑こんなsameCheckっていう関数があるとして」
娘「この関数の、戻り値の型を求めることはできる?」

よめ太郎「できるで」

type SameCheckReturnType = ReturnType<typeof sameCheck>

よめ太郎「↑こうや」

娘「へえ、ReturnTypeってやつにtypeof sameCheckを渡すと」
娘「sameCheck関数の戻り値の型を取得できるんだ!」

よめ太郎「せや」
よめ太郎「今回の場合、SameCheckReturnType型はboolean型になるっちゅうことやな」

娘「なるほどね〜」

よめ太郎「もしsameCheck関数の戻り値の型が変わった場合でも」
よめ太郎「SameCheckReturnType型も追従して変わってくれるから」
よめ太郎「ベタ書きでbooleanって書くより便利やで」

娘「なるほどね!」
娘「ある関数の型を変更した場合に」
娘「連動して他の部分の型も変わってくれるってことだね」
娘「たまに便利そうだね!」

よめ太郎「そういうことや」

Parameters<Type>

よめ太郎「逆に、ある関数の引数の型を取得したい場合は」

type SameCheckParametersType = Parameters<typeof sameCheck>

よめ太郎「↑こうや」

娘「へえ、sameCheck関数には引数が2つあるけど」
娘「どんな形式で返ってくるの?」

type SameCheckParametersType = [a: number, b: number]

よめ太郎「↑こんな感じや」

娘「へぇ、引数の型はタプル型で返ってくるんだね」

よめ太郎「せや」

Exclude<Type, ExcludedUnion>

娘「じゃあママ」

type Food = "カレー" | "オムライス" | "ラーメン" | "餃子"

娘「↑このFood型から、中華料理だけを除外したい場合は?」

type FoodExcludeChinese = Exclude<Food, "ラーメン" | "餃子">

/*
FoodExcludeChinese型は
"カレー" | "オムライス"
となる
*/

よめ太郎「↑こうやな」

type Chinese = "ラーメン" | "餃子"
type FoodExcludeChinese = Exclude<Food, Chinese>

よめ太郎「↑こうでもええで!」

娘「なるほど〜」
娘「Excludeって、除外するって意味だもんね!」

Extract<Type, Union>

娘「じゃあママ」

type Hoge = 1 | 2 | 3 | 'a' | 'b' | 'c' | true | false

娘「↑このHoge型から、truefalseだけ除外したい場合は?」

よめ太郎「ええと、要はbooleanを除外すればええから」
よめ太郎「さっきのExcludeを使って・・・」

type HogeStringNumber = Exclude<Hoge, boolean>

/*
HogeStringNumber型は
1 | 2 | 3 | "a" | "b" | "c"
となる
*/

よめ太郎「↑こうやな」
よめ太郎「もしくは」
よめ太郎「Hoge型から数値と文字列だけ抽出した型を作る、と考えて」

type HogeStringNumber = Extract<Hoge, string | number>

よめ太郎「↑こうでもええで」
よめ太郎「Extract抽出するって意味やからな」

娘「へえ〜」

型関数

娘「すごいね」
娘「引数として型を渡すと、別の型が返ってくる・・・」
娘「型を作る関数みたいだね!」

よめ太郎「まさにそんな感じやな」
よめ太郎「せやから、型関数って呼んどる人たちもおるで」
よめ太郎「正式な用語やないらしいけどな」

娘「へぇ〜、型関数か〜」

よめ太郎「TypeScriptの公式サイトでは、Utility Typesって呼ばれてるみたいや」
よめ太郎「お役立ち型って意味やな」
よめ太郎「その名の通り、けっこう実務で役立つから」
よめ太郎「こんなのがある、ってことだけでも覚えておくとええで!」

娘「わかった!」
娘「ありがとう、ママ!」

よめ太郎「どういたしましてやで!」

娘「あっ!パパ!」

ワイ「なんや?娘ちゃん」

娘「ウンチ出た!」

ワイ「いやそこは赤ちゃんなんかい!」

〜おしまい〜

参考文献

この記事で書いた以外にも色々便利なのがあるから、是非読んでみてくださいやで!

新しい記事もよろしくやで

5歳娘「パパ、型はドキュメントだよ?」

555
328
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
555
328

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?