💡

殺伐とした「TypeScript全然わからんぼく」に救世主(サバイバルTypeScript)が!

2022/08/26に公開

TypeScript素人あるある「よくわからんポイント」だいたい解決しました

  • typeで&する時とinterfaceでextendsする時の違いわからん
  • ちょっと複雑になるともう読めないアロー関数、ジェネリクスとか

経緯

先日購入した、TypeScriptとReact/Next.jsでつくる実践Webアプリケーション開発

TypeScript
とReact/Next.jsでつくる実践Webアプリケーション開発 表紙

大ボリュームで非常に学びが多い名著なのですが、紙面の都合上、解説が省かれている箇所が多いです。

TypeScriptほぼ初めて(JavaScriptもなんとなくだった)なぼくには
難易度が高い記述も多い本です。

特に6章の実装部分はコード量・質ともに圧倒されます。

現場レベル、ということでしょうか。
学習のために、わざといろいろな記述を見せてるのでしょうか。

正直、心が折れかけてました。

そこに救世主が現れたのです。

「TypeScriptぜんぜんわからない・・・」と
ツイートしてたら、サバイバルTypeScriptの中の人が
いいねしてくれました。

https://typescriptbook.jp/

軽く中身を見てみると、
前述したぼくの「よくわからんポイント」が
詳細に解説 してあります。
今日はこれ読み込んでみます。

正直、ぼくの記事の続きを読むより、
サバイバルTypeScriptを読んだ方が幸せになれますが、

TypeScriptとReact/Next.jsでつくる実践Webアプリケーション開発
同様に躓いている人もいるかと思うので、

この記事では本書のコードを例に、
躓いた箇所だけざっくりまとめます。

(本書の6.5章で止まっているので、追記する可能性あります)

以下本題

typeとinterfaceって何が違うのよ!

例えば、

/src/components/atoms/Button/index.tsx
// ボタンのバリアント
export type ButtonVariant = 'primary' | 'secondary' | 'danger'

export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
  variant?: ButtonVariant
  fontSize?: Responsive<FontSize>
  /* 中略 */
}

ふむふむ、特定の型を拡張するには
T & {拡張したいkey: value, key2: value2, ...}
みたいに書けるんだな。

なるほど、TypeScript理解した。

・・・と思うじゃん?

/src/components/atoms/RectLoader/index.tsx
interface RectLoaderProps extends IContentLoaderProps {
  width: number
  height: number
}

はぁ?

typeの&と、interfaceのextendsの違い、何???

答えは、
interfaceとtypeの違いに書いてあります。

  • 同名のkeyを宣言すると、interfaceはオーバーライド(上書き、できないときくはコンパイルエラー)、typeはneverになる
  • 同名のtype名を宣言しようとするとエラー、interfaceはエラーにならず(!?)マージされる

といった違いがあります。

同名interfaceにさえ気をつければ、
意図せずneverになることは防げるかな・・・?

ちょっと複雑だともう読めないアロー関数

TypeScriptじゃなくて、JavaScriptの話です。

アロー関数? 知ってる知ってる、

普通の関数だと、

function AddNum(a: number, b: number) {
  return a + b
}

みたいなのを

const AddNum = (a: number, b: number) => a + b

みたいに省略して書けるやつでしょ?
TypeScript(というかJavaScript)理解したわ!

・・・と思うじゃん?

/src/components/atoms/IconButton/index.tsx
function withIconStyle(
  Icon: typeof SvgIcon,
): React.ComponentType<IconButtonProps> {
  const IconWithStyle = (props: IconButtonProps) => {
    const { onClick, className, size = 24, ...rest } = props
    /* 中略 */
    return (
      /* 中略 */
    )
  }

  return IconWithStyle
}

ちょっと複雑になると、もう、読めん・・・

答えは、
アロー関数
関数宣言
関数式

に、だいたい書いてありますが、

function 関数名(引数: 引数の型): 戻り値の型 {
  const 変数 = (引数: 引数の型) => {
    /* 処理 */
  }
  return 変数
}

ということをしている。

アロー関数の基本構文は

(引数) => {処理}

なんだけど、ここで引数の型をつけたり、
戻り値の型を指定したり、
処理がreturnしかないなら、()で囲えば省略できたり、
処理が1行ですむなら{}も省略できたりする。(柔軟すぎる・・・)

実際、サンプルコードのAppLogoは、引数いらないので、
ここまで省略できています

/src/components/atoms/AppLogo/index.tsx
const AppLogo = () => (
  /* 中略 */
)

あとはthisの指すものが違ったり、

普通の関数ではarguments変数を使うところを
残余引数...(後述)を使うところだったり、

細かなところが違うことを覚えておこうと思います。

特に理由がない場合はアロー関数を使っておくのが無難らしいです。

{ arg1, arg2, ...rest } = props ←これ何!?

残余引数
残余構文
あたりに書いてあります。

コード読めば推測できる通りの挙動なんだけど、
...restは、その左に列挙した要素の他の、残り(残余)
要素全部取り出してくれる。

見出しの式なら、右辺propsの
arg1,arg2,以外の全部をまるっと持っていてくれる。

既存の型を&やextendsで拡張し、拡張したHTML要素を書く場合、
既存の型で対応可能な要素をそのままうけとりたいため、
Atomsの記述では頻出します。

return <div /* 拡張した要素 */ {...rest /* ←拡張してない要素全部 */ } />

みたいに気軽に書けます。便利すぎか。

わかったようでわからないジェネリクス型

型<型>って何?
型の宣言部分なのに、さらに型を宣言しているよく分からない子。(ぼく、Java経験者のくせに)

答えはジェネリクスに、
例もふまえてわかりやすく書いてあります。

「引数」も「戻り値」も同じ型の場合のバリエーションを作りたい場合、
型をまるで引数のように扱ったコードが作れます。

/src/components/atoms/IconButton/index.stories.tsx
const Template: ComponentStory<typeof SearchIcon> = (args) => (
  <>
    <SearchIcon {...args} />
    <CloudUploadIcon {...args} />
    <PersonOutlineIcon {...args} />
  </>
)

↑で、SearchIconにVSC上でカーソルあわせると
SearchIconはComponentType<IconButtonProps>
つまり、IconButtonPropsをプロパティにもつコンポーネントだよー、と
教えてくれます。

(args)にVSCでマウスカーソルをあわせると、
typeはIconButtonPropsだよーと教えてくれます。

ここのストーリーではSearchIconのtypeを引数に指定しているが、
CloudUploadIconもPersonOutlineIconも、同じtypeなので
使い回せています。

うむむ、storiesはやってること複雑なので
例には向かなかったです。やっぱ、
ジェネリクスの項
読んでください()

他にも、、、

Omit<T, key>以外にも似たような便利すぎる記述法がある、
値に関数を代入できる理由、
などなど、かゆいところに手が届く説明が多い。

何よりありがたいのが、
全体的に説明が端的、簡潔、分かりやすい!

文法の基礎を学ぼうとすると、時に

  • 他の言語との比較とか、
  • その言語仕様が生まれた文化とか、

長過ぎるウザ語り短気なエンジニアには丁寧すぎる説明が
登場することが多いが、サバイバルTypeScriptはほぼそんなことない。

今、困ったことが今、解決できるのがありがたいです。

結論

「TypeScriptぜんぜんわからん」状態になったときの救世主!

https://typescriptbook.jp/

GitHubで編集を提案

Discussion