わいの日記

ありがちエンジニアブログ

TypeScript (Constructor)

こういう差があるんだ。。。
constructorの引数でpublicとかprivateの修飾詞つけると、
わざわざ別に変数宣言しなくてもいいんだ
へぇ

// これだとこれだとtitleがないよと怒られる
// class Category {
//     constructor(title: string) {}
//     getInfo(): void {
//         console.log(this.title)
//     }
// }

class Category1 {
    private title: string // ここでtitleを宣言しておくと大丈夫
    constructor(title: string) {}
    getInfo(): void {
        console.log(this.title)
    }
}

class Category2 {
    // publicややprivateなど付けておくと勝手にマッピングしてくれてエラーが出ない
    constructor(private title: string) {}
    getInfo(): void {
        console.log(this.title)
    }
}

https://amzn.to/2IYey2j

Amazon CAPTCHA

TypeScript カスタムエラーの作成の仕方

Errorを丸投げはよくない
Errorを実装したApplicationErrorみたいなのを作成して、
そこから具体的なエラークラスを切り出していくのがいい感じみたい

サンプルコード

// ErrorオブジェクトをApplicationErrorって大きいくくりで実装する
class ApplicationError implements Error {
    // エラー名を設定
    public name = 'ApplictionError';

    // エラーメッセージを引数にとる
    constructor(public message: string) {
        // consoleがあるかないかを確認しとく
        if (typeof console !== 'undefined') {
            console.log(`name: ${this.name}, message: ${this.message}`)
        }
    }
    //  toStringを適当な形で上書きしておく
    toString() {
            return `${this.name} ${this.message}`;
    }
}

// 権限系のエラー
class PermissionError extends ApplicationError {
    // エラーの名前は設定しておく
    public name = 'PermissionError';
}

//  ユーザー作成系のエラー
class UserCreateError extends ApplicationError {
    public name = 'UserCreateError';
}


function notPermitted(isAdmin: boolean) {
    if (!isAdmin) {
        throw new PermissionError('not permitted')
    }
}

try {
    notPermitted(false)
} catch (error) {
    // エラーのタイプで処理を切り分ける
    if (error instanceof PermissionError) {
        // 省略
    }
    if (error instanceof UserCreateError) {
        // 省略
    }
}

具体的に処理内容ごと、エラー内容ごとにエラーハンドリングがしやすくなったり、
バグ調査が楽になりそうでいいなと思いました

Amazon CAPTCHA

https://amzn.to/2V8522X

TypeScript (Type Guards、Discriminated Unions)

TypeScriptいろいろできるなー

Type Guards

たとえばstring型の数字か,numberの数字を引数に取る想定した場合とかに有効かな

const argNumberOrString = (value: string | number): void => {
    value.toString() // ここはセーフ
    value.toFixed() // ここでvalueはstring型と見なされ、toFixedなんてstringにはないよとエラーになる

   // Type Guardsでそれぞれの型が合うように条件分岐させる
    if (typeof value === 'string') {
        value.toString()
    }

    if (typeof value === 'number') {
        value.toFixed()
    }
}

Discriminated Unions

literal型のメンバを使用して、ユニオン型のメンバを特定するのに役立つ
今回はkindでDogかCatを判別できるようにする

>>|
// 文字列指定する
interface Dog {
kind: 'dog',
yelp: () => string
}

interface Cat {
kind: 'cat',
meow: () => string
}

type Pet = Dog | Cat; // Pet型はDogかCatのどちらか

const getVoice = (pet: Pet): string {
// Type Guard
switch (pet.kind) {
case 'dog':
return pet.yelp()
case 'cat':
return pet.meow()
default:
return 'hoge'
}
}
|

switch文で書くとなんとなくreduxのreducerみがある気がする

amzn.to

amzn.to

TypeScript (Intersection Types、 Mapped Types)

へぇと思ったので紹介

Interscetion Types

両方引き継ぐことができる

でorができるんだから、&も当然っちゃ当然か
interface IHoge {
    hoge(): void
}

interface IFuga {
    fuga(): void
}

// Intersection Types
type IHogeFuga = IHoge & IFuga

let hogefuga: IHogeFuga = undefined;

f:id:exhikkii:20190430234809p:plain
予測でhogeメソッドとfugaメソッドが表示されている

Mapped Types

// 全てのプロパティをreadonlyにする
type ReadOnly<T> = { readonly [k in keyof T]: T[k]; }

// 全てのプロパティを任意項目にする
type Optional<T> = { [k in keyof T]?: T[k]; }

// 全てのプロパティをnull許容する
type Nullable<T> = { [k in keyof T]: T[k] | null; }

// テキトーなinterfaceを用意する
interface ITodo {
    id: number;
    content: string;
    isCompleted: boolean
}

// ITodoに充てる
type ReadOnlyTodo = ReadOnly<ITodo>
type OptionalTodo = Optional<ITodo>
type NullableTodo = Nullable<ITodo>

// 全てのプロパティがreadonly
const todo1: ReadOnlyTodo = {
    id: 1, // strictNullChecksががtrueならならnullを使えない
    content: 'content',
    isCompleted: false
}

// 全てのプロパティを任意項目だからこれでエラー出ない
const todo2: OptionalTodo = {}

// 全てのプロパティをnull許容にする
const todo3: NullableTodo = {
    id: null,
    content: null,
    isCompleted: null
}

2冊目がわかりやすいよ
1冊目は出版社がよくセールしてる印象ある

https://amzn.to/2GJrPrFamzn.to

https://amzn.to/2DEyK4Jamzn.to

TypeScriptいろいろ(型、継承、Interface、Singleton)

整理整理

Interface 継承

interface IFather {
    fatherName: string // おとんの名前をstring型
}

interface IMother {
    motherName: string // おかんの名前をstring型
}

// IFatherとIMotherを継承するIChildrenを定義
interface IChildren extends IFather, IMother {
    name: string
   age?: number // ? は任意項目
}

const child = <IChildren>{
    fatherName: 'tetsu',
    motherName: 'yoshie',
    name: 'chie'
   // age は任意なので設定しなくてもよい
}

関数にもinterfaceを
引数と返り値に型付けを

interface ICalc {
    (value: number): number;
}

const calcDouble: ICalc = (value: number) => value * 2

Singleton

class Person {
    private static _instance: Person;
    private constructor() { }
    private _name: string;

    static getInstance(): Person {
        if (!Person._instance) {
            Person._instance = new Person();
        }
        return Person._instance
    }

    get name(): string {
        return this._name
    }

    set name(name: string) {
        this._name = name; 
    }

}
const person = Person.getInstance();
console.log(person)
person.name = 'wai'
console.log(person.name)

継承の注意点

// IMyArrayを定義する
interface IMyArray {
    // indexがnumberでその値がstring型
    [index: number]: string;
}

// IMyArray型の配列を定義する
const days: IMyArray = ["Mon", "Tue"]

days.push("Wed") //IMyArrayにpushメソッドなんてないとエラーが起こる

解決策

// IMyArrayがArray型を継承するようにする
interface IMyArray extends Array<string> {
    [index: number]: string;
}

const days: IMyArray = ["Mon", "Tue"]

days.push("Wed") 

ふむふむ
もっと勉強しないと

amzn.to

amzn.to

( React + TypeScript)event.target.nameとevent.target.valueの型付け

最近、React や TypeScriptを使って開発してるので、書いてみます

Reactのフォームを書くときに、
イディオム的に下記のように
event.target.nameとevent.target.valueの組み合わせをすることがあると思います

interface User {
    name: string,
    age?: number,
    email: string
}

const [user, setUser] = useState<User>({ name: '', email: '' });

const handleChange = (event: /* TODO ここに何を書く? */) => {
    setUser({
        ...user,
        [event.target.name]: event.target.value}
    )
}

return (
    <form onSubmit={/* 省略 */}>
        <input type="text" name="name" value={user.name} onChange={handleChange}/>
        <input type="number" name="age" value={user.age} onChange={handleChange}/>
        <input type="text" name="email" value={user.email} onChange={handleChange}/>
        <button type="submit">Submit</button>
    </form>
)

上記のhandleChangeのeventの型をどうするかで少し迷いました

ちなみに型を何も付けないと、下のようなエラーが出ます

f:id:exhikkii:20190428220007p:plain

じゃあevent: HTMLInputElementではどやって考えたのですが、
次はtargetでひっかかる。。。

f:id:exhikkii:20190428215928p:plain
target...

結論を言うと、React.ChangeEventを使えばうまく行きます

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setUser({
        ...user,
        [event.target.name]: event.target.value}
    )
}

TypeScriptは流行ってるようやから、おさえておきたい

amzn.to

amzn.to

Nuxt.js に pugを導入する

Nuxt.jsのプロジェクトでpugを導入したので、紹介をします


pugとは?
閉じタグがなくて、
インデントを整えて書いていくテンプレートエンジンですね

pugjs.org


Nuxt.jsへのpugの導入はめっちゃ簡単です
公式にも書いてるしね

プリプロセッサ - Nuxt.js

yarn add pug pug-plain-loader

これだけ
pugをloaderをyarn addするだけ
特に設定ファイルを用意する必要もありません

次にpugで書いた例を

下がプロジェクトを作成したばかりの
Nuxt.jsのトップページにあたるデフォルトのindex.vue

<template>
  <section class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt-pug
      </h1>
      <h2 class="subtitle">
        My amazing Nuxt.js project
      </h2>
      <div class="links">
        <a href="https://nuxtjs.org/" target="_blank" class="button--green"
          >Documentation</a
        >
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
          >GitHub</a
        >
      </div>
    </div>
  </section>
</template>

これをpugで書き直すと

<template lang="pug">
  //- templateタグでlang="pug"と指定する
  section.container
    div
      logo
      h1.title nuxt-pug
      h2.subtitle My amazing Nuxt.js project
      .links
        a.button-green(href="https://nuxtjs.org/" target="_blank") Documentation 
        a.button--grey(href="https://github.com/nuxt/nuxt.js" target="_blank") GitHub
</template>

めっちゃスッキリ!
pugいいよ〜

amzn.to