世界一適当な技術ブログ

日々学んだ内容をとにかくにブログ形式でアウトプットします。(技術系中心)基本自分用備忘録です

break, return, nextまとめ

break, return, nextまとめ

たまにこの辺りの使い分けややこしくなるのでまとめます。

break

breakを使うと、繰り返し処理を抜けることができます。

def block_break
  puts '----'
  (1..5).each do |i|
    break if i % 3 == 0
    puts i
  end
  puts 'end'
end
1
2
end

return

returnを使うと、メソッドを抜けることができます。

def block_return
  puts '----'
  (1..5).each do |i|
    return if i % 3 == 0
    puts i
  end
  puts 'end'
end
1
2

next

nextを利用すると、ループは抜けずに次の処理にスキップする

def block_next
  puts '----'
  (1..5).each do |i|
    next if i % 3 == 0
    puts i
  end
  puts 'end'
end
1
2
4
5
end

繰り返し処理で使うbreakとreturnの違い

breakを使うと「繰り返し処理からの脱出」になりますが、returnの場合は「(繰り返し処理のみならず)メソッドからの脱出」になります。 たまにこれがごっちゃになりバグに繋がる時があるので気をつけましょう。

break, return, next違いまとめ

next: 以降は実行せず、次のループを開始
break: ループを抜ける。
return: メソッドを抜ける

参考

ブロックについてのあれこれ - Qiita

docker-syncについてのまとめ

docker-sync is 何?

Docker利用時にホスト側のディレクトリをマウントして使うと猛烈に遅いという現象が発生します。

Docker syncを利用することで速度改善することが可能になります。

docker-syncの仕組みとしては、 docker の -v で直接ホスト側のディレクトリをマウントするのではなく、rsyncやunisonの仕組みでホスト側のファイルをコンテナ側に転送しよう、というものです。

今回はホスト側のファイルのコンテナ転送方法にunisonを利用します。

unisonインストールはhomebrewを利用します。

brew install unison

同様に docker-syncもインストールします。

gem install docker-sync

参考: Installation — docker-sync 0.5.11 documentation

これでdocker-syncを使う準備は完了です。

docker-syncの設定方法

参考:docker-syncでファイルの同期を高速化する | RE:ENGINES

docker-composer.yml

docker-composer.yml は、特別な設定は不要です。

こちらは通常通り、docker-composer.ymlを作成してください。

docker-syncの設定は後述するdocker-sync.ymlに設定していきます。

version: "3"
services:
  hogeapp:
    build:
      context: .
      dockerfile: Dockerfile-hoge.local
  fugaapp:
    build:
      context: .
      dockerfile: Dockerfile-fuga.local

docker-composer-dev.yml

docker-composer-dev.yml は、ボリュームとマウントのオーバーライドを定義します。

version: "3"
services:
  hogeapp:
    volumes:
      - hogeapp-sync:/var/www/hoge:nocopy (イメージのビルド中の内容とマージされてしまうためnocopyをつける)
  fugaapp:
    volumes:
      - fugaapp-sync:/var/www/fuga:nocopy
 
volumes:
  hogeapp-sync:
    external: true (docker-sync側で自動で作ってくれるボリュームをexternalとして宣言)
  fugaapp-sync:
    external: true

docker-sync.yml

docker-sync自体の設定は docker-sync.yml に書いていきます。

docker-sync.ymlは非常に優秀で、既存のdocker-compose.ymlを自動で読み取ります。

既存のdocker-compose.ymlに上書きしたい箇所をyamlで記載します。

version: "2"
syncs:
  hogeapp-sync:
    src: './app/hoge'
  fugaapp-sync:
    src: './app/fuga'

参考サイト

docker-sync by EugenMayer

docker-syncでホスト-コンテナ間を爆速で同期する - Qiita

docker-syncでファイルの同期を高速化する | RE:ENGINES

TypeScriptでのクラスの取り扱い方について

// 継承にのみ利用できるクラスにはabstractを利用
abstract class Person {
  // インスタンス作らずにクラスを使う場合はstaticを利用する
  // 例 Person.species
  static species = 'Homo sapiens';
  // インスタンス作らずにクラスを使う場合はstaticを利用する
  // 例 Person.isAdult(38)
  static isAdult(age: number) {
    if (age > 17) return true;
    return false;
  }
  // constructorで初期化できる
  // 書き換えできないようにするにはreadonly
  // 継承先のみでアクセスできるようにするにはprotectedにする
  constructor(readonly name: string, protected age: number) {
  }
  // メソッド追加もできる
  incrementAge() {
    this.age += 1;
  }
  // クラスを型に定義することもできる
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
    this.explainJob();
  }
  abstract explainJob(): void;
}

// Classはオブジェクトの設計図(Classから作成されたオブジェクトはインスタンスになる)
// extendsでPersonを継承できる
class Teacher extends Person {
  private static instance: Teacher;

  explainJob() {
    console.log(`I am a teacher and I teach ${this.subject}.`);
  }

  // ゲッター処理
  get subject(): string {
    if (!this._subject) {
      throw new Error('There is no subject.');
    }
    return this._subject;
  }

  // セッター処理
  set subject(value) {
    if (!value) {
      throw new Error('There is no subject.');
    }
    this._subject = value;
  }
  // クラスの外側で変更できない場合はprivateにする
  // privateをつけるとシングルトンパターンになって、インスタンスを一つしか利用できないようにする事が可能
  private constructor(name: string, age: number, private _subject: string) {
    // 親クラスのconstructorを引き継ぐ
    super(name, age);
  }
  static getInstance() {
    if (Teacher.instance) return Teacher.instance;
    // newでTeacherクラスのインスタンスを作成する
    Teacher.instance = new Teacher('Quill', 38, 'Math');
    return Teacher.instance;
  }
}
const teacher = Teacher.getInstance();
const teacher2 = Teacher.getInstance();
console.log(teacher, teacher2);

TypescriptのGenerics

// オブジェクトのキーのunion型を作る
// U extends keyof Tのようにすると、Uの型は、name | ageのunion型になります。
function copy<T extends { name: string }, U extends keyof T>(value: T, key: U): T {
  value[key];
  return value;
}
console.log(copy({ name: 'Quill', age: 20}, 'age'));

// classに対してgenericsを使う方法
class LightDatabase<T extends string | number | boolean> {
  private data: T[] = [];
  add(item: T) {
    this.data.push(item);
  }
  remove(item: T) {
    this.data.splice(this.data.indexOf(item), 1);
  }
  get() {
    return this.data;
  }
}
const stringLightDatabase = new LightDatabase();
stringLightDatabase.add('Apple');
stringLightDatabase.add('Banana');
stringLightDatabase.add('Grape');
stringLightDatabase.remove('Banana');
console.log(stringLightDatabase.get());

// Utility Types(型のlibrary)
interface Todo {
  title: string;
  text: string;
}
// 全部optional型になるUtility class
type Todoable = Partial<Todo>
// Readonly classだったら、全てにreadonly
type ReadTodo = Readonly<Todo>

const fetchData: Promise<string> = new Promise(resolve => {
  setTimeout(() => {
    resolve('hello');
  }, 3000);
})
fetchData.then(data => {
  data.toUpperCase();
})
const vegetables: Array<string> = ['Tomato', 'Broccoli', 'Asparagus'];

interface ResponseData<T extends { message: string } = any> {
  data: T;
  status: number;
}
let genericsTmp2: ResponseData;
interface Vegetables {
  readonly tomato: string;
  pumpkin?: string;
}
let genericsTmp3: keyof Vegetables;
// readonlyの頭に ー を付けると、Vegetablesのreadonlyが全て打ち消す
type MappedTypes = {
  -readonly [P in keyof Vegetables]-?: string
}
// tomato型が、string型に代入できるのであれば、number、できないのであればboolean
type ConditionalTypes = 'tomato' extends string ? number : boolean;
type ConditionalTypesInfer = { tomato: string } extends { tomato: infer R } ? R : boolean;
type DistributiveConditionalTypes<T> = T extends 'tomato' ? number : boolean;
let genericsTmp4: DistributiveConditionalTypes<'tomato' | 'pumpkin'>

Typescriptの応用

// ------------- インターセクション -------------
// エンジニアの型を定義
interface Engineer {
  name: string;
  role: string;
}
// ブロガーの型を定義
interface Blogger {
  name: string;
  follower: number;
}
// type EngineerBlogger = Engineer & Blogger;
// extendsでinterfaceを継承する事ができる
interface EngineerBlogger extends Engineer, Blogger { }

const quill: EngineerBlogger = {
  name: 'Quill',
  role: 'front-end',
  follower: 1000
}

// 型を複数条件定義することも可能
type NumberBoolean = number | boolean;
type StringNumber = string | number;
type Mix = NumberBoolean & StringNumber;

// ------------- type guard -------------
// 関数のオーバーロード(引数がsringの場合はstringを返却する)
function toUpperCase(x: string): string;
// 関数のオーバーロード(引数がnumberの場合はnumberを返却する)
function toUpperCase(x: number): number;

// 型によって、処理を分ける場合はこのように書く(type ofを利用する)
function toUpperCase(x: string | number): string | number {
  if (typeof x === 'string') {
    return x.toUpperCase();
  }
  return x;
}

// ------------- オーバーロードを型で表現する -------------
interface TmpFunc {
  (x: string): number;
  (x: number): number;
}
//string と numberに対応した型の関数しか使えない
const upperHello: TmpFunc = function (x: string | number) { return 0 };

// ------------- 関数型のIntersection -------------
interface FuncA {
  (a: number, b: string): number;
}
interface FuncB {
  (a: string): string;
}

// (a: number, b: string): number;
// (a: string, b: number): number;
// (a: string): number;
let unionFunc: FuncA | FuncB;

// ------------- type guard(in演算子) -------------
// typeof以外での条件分岐方法
type NomadWorker = Engineer | Blogger;
// 型がNomadWorkerの関数を作る
function describeProfile(nomadWorker: NomadWorker) {
  console.log(nomadWorker.name);
  // nomadWorkerのキーにroleがあるかどうかで条件分岐する
  if ('role' in nomadWorker) {
    console.log(nomadWorker.role);
  }
  // nomadWorkerのキーにfollowerがあるかどうかで条件分岐する
  if ('follower' in nomadWorker) {
    console.log(nomadWorker.follower);
  }
}
// ------------- type guard(instanceof) -------------
class Dog {
  kind: 'dog' = 'dog'
  speak() {
    console.log('bow-wow');
  }
}
class Bird {
  kind: 'bird' = 'bird';
  speak() {
    console.log('tweet-tweet');
  }
  fly() {
    console.log('flutter');
  }
}
type Pet = Dog | Bird;
function havePet(pet: Pet) {
  pet.speak();
  switch (pet.kind) {
    case 'bird':
      pet.fly();
  }
  if (pet instanceof Bird) {
    pet.fly();
  }
}
havePet(new Bird());


// ------------- Optional Chaining -------------
//  あってもなくてもいい場合は?をつける
interface DownloadedData {
  id: number;
  user?: {
    name?: {
      first: string;
      last: string;
    }
  }
}

// DownloadedDataを型にもつ
const downloadedData: DownloadedData = {
  id: 1
}

// downloadedData.user?.nameは、
// downloadedData.userがundefinedもしくはnullだったら、undefinedを返す、
console.log(downloadedData.user?.name?.first);

// ------------- Nullish Coalescing -------------
const userData = downloadedData.user ?? 'no-user';
type id = DownloadedData["id"]

enum Color {
  RED,
  BLUE
}
class AdvancedPerson {
  name: string = 'Peter'
  age: number = 35;
}
class AdvancedCar {
  name: string = 'Prius'
  age: number = 5;
}
let target = new AdvancedPerson();
let source = new AdvancedCar();
target = source;
function advancedFn(...args: readonly number[]) {
}

advancedFn(0, 1)
const milk = 'milk' as const;
let drink = milk;
const array = [10, 20] as const;
const peter = {
  name: 'Peter',
  age: 38
} as const;
type PeterType = typeof peter;

````

TypeScriptのInterfaceについて

TypeScriptのInterfaceについて

interfaceは、オブジェクト・関数・クラスの構造を決めるためのもの。

サンプルコード

// 関数の型を定義することも可能
interface addFunc {
  (num1: number, num2: number): number;
}
let addFunc: addFunc;
addFunc = (n1: number, n2: number) => {
  return n1 + n2;
}
interface Nameable {
  //nullableな値には?をつける
  name?: string;
  nickName?: string;
}
const nameable: Nameable = {
  name: 'Quill',
  nickName: 'Quilla'
}
// interfaceを継承する場合はextendsを利用する(Nameableを継承する)
// extendsを使うとinterfaceからinterfaceへ継承できます。
interface Human extends Nameable {
  age: number;
  greeting(message: string): void;
}

// implementsを使用すれば、クラスに対してinterfaceの条件適用が可能になる
class Developer implements Human {
  // constructor メソッドは、 class で作成されたオブジェクトの生成と初期化のための特殊なメソッド
  constructor(public age: number, public experience: number, public name?: string) {
  }
  greeting(message: string) {
    console.log(message);
  }
}

const user: Human = new Developer(38, 3);

const tmpDeveloper = {
  name: 'Quill',
  age: 38,
  experience: 3,
  greeting(message: string) {
    console.log(message);
  }
}

if (user.name) {
  user.name.toUpperCase()
}
console.log(user.name);

参考

Typescriptのinterfaceの使い方 - Qiita

インターフェース - TypeScript Deep Dive 日本語版

Reactコンポーネントの違い

Reactコンポーネントの違い

Reactでも勿論コンポーネントを利用することは可能です。

コンポーネントを利用することで、再利用が楽になる、コードの見通しがよくなる、変更に強い設計にすることが可能になります。

Class Component

f:id:lhiroki1205:20210723171850p:plain

Functional Component

f:id:lhiroki1205:20210723171853p:plain こちらの方が主流のコンポーネントの書き方。 Functional Componentの方が記述量が少ない。

propsでデータを受け渡す方法

コンポーネントから子コンポーネントにデータを受け渡したい場合はpropsでデータ受け渡しが可能になります。

■ 親コンポーネント f:id:lhiroki1205:20210723172947p:plain

■ 子コンポーネント f:id:lhiroki1205:20210723172950p:plain

※ 受け渡せるデータは文字列、数値、真偽値、配列、オブジェクト、日付などなんでもOK

参考

#04 新・日本一わかりやすいReact入門【基礎編】コンポーネントとprops - YouTube