Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TypeScriptだったらFEとBEどっちも書けるし、楽じゃない? TypeScripter vs PHPerの戦い

玉山
October 08, 2023

TypeScriptだったらFEとBEどっちも書けるし、楽じゃない? TypeScripter vs PHPerの戦い

玉山

October 08, 2023
Tweet

More Decks by 玉山

Other Decks in Programming

Transcript

  1. INDEX 1 .自己紹介 2 .発表の経緯 3 .PHPとTypeScriptとは 4 .型安全性の比較 ©

    kaonavi, inc. 2 5 .ユニットテストの書きやすさ比較 6 .APIを作る上での優位性の比較 7 .まとめ
  2. たまやま りお tamayama rio 自己紹介 © kaonavi, inc. 4 所属

    株式会社カオナビ 2020年度新卒入社 職種 バックエンドエンジニア X https://x.com/tama_php
  3. 企画職 Product Manager Product Owner Director エンジニア職 Back-end Engineer Front-end

    Engineer Native Application Engineer QC Engineer SRE Infrastructure Engineer DevOps Engineer デザイナー職 UI/UX Designer © kaonavi, inc. 8 一緒に働く仲間を募集しています
  4. フロントエンドの人がバックエンドやるスイッチングコス ト無い方がよくない? • FEは破壊メソッドはあまり使わない • JSではこう書くけどPHPだとどうやって書くんだっけ? • TSだとnumberだけどPHPはint, floatとかたくさんある •

    constとかつけるけどPHPは変数宣言のキーワードは存在しない? 
 • 変数のスコープの概念が違うため、置き場所わからん 
 • などなど...
 発表の経緯 © kaonavi, inc. 13
  5. • 1995年 ◦ 開発が開始 • 1995年 6月 ◦ PHPの元祖であるPHP Toolsが誕生

    • 1998年 ◦ PHP 3.0 が公開 ◦ ElePHPantのロゴが作られる • 2011年 ◦ Laravel 1の誕生 • 2022年 12月 ◦ 現在では8.2が最新バージョン PHPとは © kaonavi, inc. 18 出典: https://www.jetbrains.com/ja-jp/lp/php-25/
  6. • 2010年 ◦ 開発が開始 • 2012年 ◦ 最初のTypeScript 0.8が公開 •

    2015年 ◦ 安定版であるTypeScript 1.0が公開 • 2016年 ◦ オープンソース化 ▪ GitHubで公開中: https://github.com/microsoft/TypeScript • 2023年 8月 ◦ 現在では5.2が最新バージョン TypeScriptとは © kaonavi, inc. 22
  7. TypeScriptとは © kaonavi, inc. 24 デメリット • 静的型付けなため制限がかかることもある • 型定義ファイルなどファイル数が少し増える

    • JavaScriptで書かれているライブラリは@typesパッケージが必要 ◦ 無い場合は.d.tsファイルを自分で作成しなければならない
  8. • 型安全性の比較 ◦ TypeScriptで書いたときのメリットって? • ユニットテストの書きやすさ比較 ◦ テストちゃんと書ける? • APIを作る上での優位性の比較

    ◦ 実際に置き換えられるの? 発表の経緯 © kaonavi, inc. 25 この2つの言語を下記観点で比較してみて置き換えが 可能か調べてみました
  9. 型安全性の比較 © kaonavi, inc. 36 function sum(a: number, b: number)

    { // Type 'string' is not assignable to type 'number'. a = '3'; return a + b; } console.log(sum(1, 2)); 数字に文字列を入れようとするパターン
  10. 型安全性の比較 © kaonavi, inc. 37 配列に対する型定義 interface Hoge { fuga:

    string; } const hoge: Hoge[] = [ { // Type 'number' is not assignable to type 'string'. fuga: 1 } ]; // Type 'number' is not assignable to type 'string'. const piyo : string[] = [1];
  11. 型安全性の比較 © kaonavi, inc. 38 interface Hoge { fuga: string;

    } const hoge: Hoge = { fuga: 'hello!' }; // Property 'piyo’ does not exist on type 'Hoge'. console.log(hoge.piyo); 型定義に無い値を取得しようとした場合
  12. 型安全性の比較 © kaonavi, inc. 39 function hoge(x: number | string)

    { if (typeof x === 'string') { // OK x.substring(1); } else { // Property 'substring' does not exist on type 'number'. x.substring(1) } // Property 'substring' does not exist on type 'string | number'. x.substring(1); } 型ガードができる
  13. 型安全性の比較 © kaonavi, inc. 41 function sum(int $a, int $b)

    { $a = '3'; return $a + $b; } var_dump(sum(1, 2)); // int(5) 数字に文字列を入れようとするパターン
  14. function sum(int $a, int $b) { return $a + $b;

    } // $a of function sum expects int, string given. var_dump(sum('1', 2)); 型安全性の比較 © kaonavi, inc. 42 数字に文字列を入れようとするパターン
  15. 型安全性の比較 © kaonavi, inc. 43 class A { // $hoge

    (array{fuga: string}) does not accept default value of type array{fuga: 123}. /** @var array{fuga: string} */ private array $hoge = ['fuga' => 123]; } 配列に対する型定義 array-shapes: https://psalm.dev/docs/annotating_code/type_syntax/array_types/#object-like-arrays
  16. 型安全性の比較 © kaonavi, inc. 44 class A { /** @var

    array{fuga: string} */ private array $hoge = ['fuga' => 'hello!']; public function get(): string { // Offset 'piyo' does not exist on array{fuga: string}. return $this->hoge['piyo']; } } 型定義に無い値を取得しようとした場合
  17. 型安全性の比較 © kaonavi, inc. 45 function format(?CarbonImmutable $date) { if

    ($date instanceof CarbonImmutable) { // OK var_dump($date->format('Y/m/d')); } else { // Cannot call method format() on null. var_dump($date->format('Y/m/d')); } // Cannot call method format() on Carbon\CarbonImmutable|null. var_dump($date->format('Y/m/d')); } 型ガードができる
  18. Mockが簡単にできる ユニットテストの書きやすさ比較 © kaonavi, inc. 50 class HogeTest extends TestCase

    { public function test(): void { $mock = $this->createMock(Hoge::class); $mock->expects($this->once())->method('getHello')->willReturn('Hello!'); var_dump($mock->getHello()); // string(6) "Hello!" } }
  19. jest • meta社製のシンプルさを重視した、快適な JavaScript テスティングフレームワーク ◦ ts-jestと@types/jestが必要 • 別々のプロセスで実行することも可能 •

    コードカバレッジも対応 • mockもできる ユニットテストの書きやすさ比較 © kaonavi, inc. 52 引用: https://jestjs.io/ja/
  20. Expectが豊富 • .toBe(value) • .toHaveBeenCalled() • .toHaveBeenCalledTimes(number) • .toHaveReturned() •

    .toBeFalsy() • .toBeNull() • .toBeTruthy() • .toBeUndefined() • .toBeNaN() • などなど… ユニットテストの書きやすさ比較 © kaonavi, inc. 53 出典: https://jestjs.io/ja/docs/expect
  21. Mockも使える • jest.spyOn() ◦ 特定のメソッドをモックに置き換える • jest.fn() ◦ mock関数として存在しない関数を作り出せる •

    jest.mock() ◦ モジュール全体をmock化できる ◦ そのファイルに書かれているすべての関数を制御下における ユニットテストの書きやすさ比較 © kaonavi, inc. 54 const spy = jest.spyOn(a, 'get').mockImplementation(() => 'Hello!'); 出典: https://jestjs.io/ja/docs/mock-function-api
  22. 今回はNestJSを選択 APIを作る上での優位性の比較 © kaonavi, inc. 58 選んだ理由 • サーバーサイドに特化している •

    DIパターンをサポートしている • Testabilityな設計 特徴 • TSで作られていて、TSで開発しやすい • Angular風 • モジュール式アーキテクチャ • 依存関係の注入ができる • アイコンが猫 +100 引用: https://nestjs.com/
  23. 今回はLaravelを選択 APIを作る上での優位性の比較 © kaonavi, inc. 60 選んだ理由 • ドキュメントなど情報が豊富にある •

    サービスコンテナ、マイグレーションなど 強力な機能が備わっている • EloquentによってDB操作を直感的にできる 特徴 • 最も利用されているPHPフレームワーク • 文法が直感的でわかりやすい • ORMが採用されている 引用: https://laravel.com/
  24. APIを作る上での優位性の比較 © kaonavi, inc. 65 function getHogeMember(): Member { //

    membersテーブルから`name`が'hoge'のレコードを取得 return Member::where('name', 'hoge')->get(); } オブジェクトリレーショナルマッピング Eloquentを使用することでDBとのやり取りを直感的にできる また、ActiveRecordパターンを採用しているので、テーブルとモデルクラスが一対一に対応して おり構造が理解しやすい
  25. APIを作る上での優位性の比較 © kaonavi, inc. 68 @Injectable() export class CatsService {

    private dogService; constructor(dogService: DogService) { this.dogService = dogService; } getCats(): { cats: string[] } { return { cats: [ 'スコティッシュフォールド ', 'マンチカン', 'アメリカンショートヘア ', 'ベンガル', this.dogService.getDogs(), ], }; } } DI Laravelの様なDIもかける しかし、Laravelみたいに自動注入はできず Moduleというものに使うサービスを設定しなけれ ばならない @Module({ controllers: [CatsController], providers: [CatsService, DogService], exports: [CatsService], }) export class CatsModule {} 出典: https://docs.nestjs.com/fundamentals/custom-providers
  26. APIを作る上での優位性の比較 © kaonavi, inc. 69 オブジェクトリレーショナル マッピング @nestjs/typeormという公式パッケージがある Entityを作ることによって、MigrateやDB操作を簡 単に行うことができます

    OneToMany, ManyToOneなどもあるため様々な リレーションもできます import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() firstName: string; @Column() lastName: string; @Column({ default: true }) isActive: boolean; } 出典: https://docs.nestjs.com/techniques/database
  27. APIを作る上での優位性の比較 © kaonavi, inc. 70 Eloquentに近い書き方ができるし、生クエリも書くことはできる where, orderBy, groupBy, limit,

    offset, joins, leftJoinAndSelect, innerJoinAndSelect, Insert, Update, Delete… const user = await getRepository(User) .createQueryBuilder("user") .where("user.id = :id", { id: 1 }) .getOne(); 出典: https://docs.nestjs.com/techniques/database