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

フロントエンドエンジニアが Python エンジニアになって見えた世界!

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for An Okina An Okina
September 25, 2025
450

フロントエンドエンジニアが Python エンジニアになって見えた世界!

PyCon JP 2025

概要
TypeScript で培った型安全性への愛着を持つフロントエンドエンジニアが、Python での開発をへて体験した「カルチャーショック」と「新たな発見」を共有します。言語の違いは単なる文法の違いではなく、思考パターンの転換でもありました。本セッションでは、JavaScript/TypeScript の世界から Python の世界へ飛び込んだ際の具体的な戸惑いと、それを乗り越えるための実践的なアプローチを紹介します。

Description:

フロントエンドエンジニアとしての歩み

言語の違いから学ぶ、たくさんのカルチャーショック

TypeScript の型システムで得ていた安心感

コンパイルエラーからランタイムエラーへの心理的な不安
型推論の恩恵を失った時の対処法
Python の動的型付けへの最初の戸惑い

「型がないこと」の自由と責任
ダックタイピングという考え方
TypeScript の == と === の違いと、Python の == と is の理解

値の比較と参照の比較の新しい切り分け
よくある落とし穴と正しい使い分け
undefined と null から None へ:「無」の概念の統一

TypeScript の 2 つの「無」から Python の 1 つの「無」へ
None の扱い方とベストプラクティス
オブジェクトの分割代入から Python アンパックへの適応

const {a, b} = objからa, b = tupleへの思考転換
複数戻り値の扱い方の違い
文字列のテンプレートリテラルから f-string への感動

より直感的になった文字列フォーマット
f-string の隠れた便利機能
camelCase から snake_case へ:命名規則の文化的違い

コミュニティの規約を尊重することの重要性
array.map() からリスト内包表記へ:Python らしい書き方の発見

リスト内包表記の美しさと落とし穴
実践!TypeScript エンジニアからの Python 型安全性戦略

mypy を使った段階的な型チェックの導入
TypeScript ライクな開発体験の構築
実際のコード比較:同じロジックを両言語で実装
まとめ:両方の世界のいいとこ取り

言語の違いを強みに変える方法
フルスタックエンジニアとしての視野の広がり

Avatar for An Okina

An Okina

September 25, 2025
Tweet

Transcript

  1. 自己紹介 ・名前: 翁 安 (オキナ アン) ・企業: Recustomer 株式会社 ・担当:

    フロントエンド 8割、バックエンド 2割 ・技術: Python, TypeScript (Next.js, React)
  2. 本セッション で話すこと話さないこと • フロントエンド開発で得た知見 • フロントエンドから見た Python の気づき • 両言語の良いところと悪いところ

    話すこと 話さないこと • Python の基本的な文法 • Python の詳細な言語仕様 • ライブラリの細かい仕様
  3. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  4. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  5. Python の動的型付けへの最初の戸惑い class Inventory: def __init__(self, stocks): self._stocks = stocks

    def count(self): return len(self._stocks) class Cart: def __init__(self, items): self._items = items def count(self): return len(self._items) Python 型安全ではない Python のダックタイピング
  6. Python の動的型付けへの最初の戸惑い class Inventory: def __init__(self, stocks): self._stocks = stocks

    def count(self): return len(self._stocks) class Cart: def __init__(self, items): self._items = items def count(self): return len(self._items) Python 型安全ではない Python のダックタイピング count メソッドを実装している
  7. Python の動的型付けへの最初の戸惑い 型安全ではない Python のダックタイピング def get_count(obj): return obj.count() object

    を渡せば count を 実行して結果を返す関数 Python class Inventory: def __init__(self, stocks): self._stocks = stocks def count(self): return len(self._stocks) class Cart: def __init__(self, items): self._items = items def count(self): return len(self._items) Python count メソッドを実装している
  8. Python の動的型付けへの最初の戸惑い 型安全ではない Python のダックタイピング def get_count(obj): return obj.count() object

    を渡せば count を 実行して結果を返す関数 Python class Inventory: def __init__(self, stocks): self._stocks = stocks def count(self): return len(self._stocks) class Cart: def __init__(self, items): self._items = items def count(self): return len(self._items) Python count メソッドを実装している 実行エラーに注意
  9. Python の動的型付けへの最初の戸惑い interface Inventory { stocks: string[] count(): number }

    interface Cart { items: string[] count(): number } const inventory: Inventory = { stocks: [], count() { return this.stocks.length }, } const cart: Cart = { items: [], count() { return this.items.length }, } TypeScript count メソッドを実装している interface Countable { count(): number } function getCount(obj: Countable): number { return obj.count() } TypeScript 型安全な TypeScript のダックタイピング
  10. Python の動的型付けへの最初の戸惑い interface Countable { count(): number } function getCount(obj:

    Countable): number { return obj.count() } TypeScript Countable で count があるのと 返り値 number が保証される interface Inventory { stocks: string[] count(): number } interface Cart { items: string[] count(): number } const inventory: Inventory = { stocks: [], count() { return this.stocks.length }, } const cart: Cart = { items: [], count() { return this.items.length }, } TypeScript count メソッドを実装している 型安全な TypeScript のダックタイピング
  11. Python の動的型付けへの最初の戸惑い 型安全な Python のダックタイピング from typing import Protocol class

    Countable(Protocol): def count(self) -> int: … def get_count(obj: Countable) -> int: return obj.count() Python class Inventory: def __init__(self, stocks): self._stocks = stocks def count(self): return len(self._stocks) class Cart: def __init__(self, items): self._items = items def count(self): return len(self._items) Python count メソッドを実装している
  12. Python の動的型付けへの最初の戸惑い 型安全な Python のダックタイピング from typing import Protocol class

    Countable(Protocol): def count(self) -> int: … def get_count(obj: Countable) -> int: return obj.count() Protocol でクラスを実装して 型安全になっている Python class Inventory: def __init__(self, stocks): self._stocks = stocks def count(self): return len(self._stocks) class Cart: def __init__(self, items): self._items = items def count(self): return len(self._items) Python count メソッドを実装している
  13. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  14. TypeScript の == と === の違いと、 Python の == と

    is の理解 - 値の比較と参照の比較の新しい切り分け - よくある落とし穴と正しい使い分け
  15. TypeScript の == と === の違いと、 Python の == と

    is の理解 TypeScript は基本的に === のみ使用する。 Python は == を基本的に使用して、 参照比較は is を使用する。
  16. TypeScript の == と === の違いと、 Python の == と

    is の理解 厳密等価演算 子 === !== 等価演算子 == != TypeScript には等価を表す演算子が2つあります
  17. TypeScript の == と === の違いと、 Python の == と

    is の理解 厳密等価演算 子 === !== 等価演算子 == != 値が同じかを比較して、暗黙的に 型変換する 5 == “5” =>  true TypeScript には等価を表す演算子が2つあります 危険 文字列の ”5” を数値に変換
  18. TypeScript の == と === の違いと、 Python の == と

    is の理解 厳密等価演算 子 === !== 等価演算子 == != 型と値が同じかを比較して、 型変換しない 5 === “5” => false 基本的に全てこちらを推奨 TypeScript には等価を表す演算子が2つあります 安全 値が同じかを比較して、暗黙的に 型変換する 5 == “5” =>  true 文字列の ”5” を数値に変換 危険
  19. TypeScript の == と === の違いと、 Python の == と

    is の理解 オブジェクトが同値かを比較する演算子 == != オブジェクトの同一性を比較する演算子 is is not Python の等価の比較演算子の場合
  20. TypeScript の == と === の違いと、 Python の == と

    is の理解 オブジェクトの中の値を比較する 基本的にこちらを使用する オブジェクトが同値かを比較する演算子 == != オブジェクトの同一性を比較する演算子 is is not Python の等価の比較演算子の場合
  21. TypeScript の == と === の違いと、 Python の == と

    is の理解 is は参照比較になります None は is を使用する オブジェクトの中の値を比較する 基本的にこちらを使用する オブジェクトが同値かを比較する演算子 == != オブジェクトの同一性を比較する演算子 is is not Python の等価の比較演算子の場合
  22. TypeScript の == と === の違いと、 Python の == と

    is の理解 TypeScript の方がシンプルですが、 イコールが1つ多い分やや冗長 == に統一できたら嬉しい! TypeScript では基本的に === しか使用しませんが、 Python では == を基本に使い、参照比較の判定だけ is を使う必要ある。
  23. TypeScript の == と === の違いと、 Python の == と

    is の理解 TypeScript の方がシンプルですが、 イコールが1つ多い分やや冗長 == に統一できたら嬉しい! TypeScript では基本的に === しか使用しませんが、 Python では == を基本に使い、参照比較の判定だけ is を使う必要ある。
  24. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  25. undefined と null から None へ:「無」の概念の統一 - TypeScript の 2

    つの「無」から Python の 1 つの「無」へ - None の扱い方とベストプラクティス
  26. undefined と null から None へ「無」の概念の統一 undefined と null 両方をケアする必要がある

    interface User { name: string started_at?: Date | null } function getStartedAt(user: User): string | undefined { if (user.started_at === undefined || user.started_at === null) { return } return user.started_at.toISOString() } ? はオプショナルです。実質 Date | undefined | null 両方をケアする
  27. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  28. オブジェクトの分割代入から Python アンパックへの適応 - const {a, b} = obj から

    a, b = tuple への思考転換 - 複数戻り値の扱い方の違い
  29. オブジェクトの分割代入から Python アンパックへの適応 TypeScript のオブジェクトの分割代入 interface User { firstName: string

    lastName: string age: number } const user: User = { firstName: 'Yamada', lastName: 'Taro', age: 20, } const { firstName, lastName, age } = user // 'Yamada', 'Taro', 20 変数名で直接扱えて便利 TypeScript
  30. オブジェクトの分割代入から Python アンパックへの適応 TypeScript のオブジェクトの分割代入 interface User { firstName: string

    lastName: string age: number } const user: User = { firstName: 'Yamada', lastName: 'Taro', age: 20, } const { firstName, …Other } = user ...変数名をスプレッド構文という firstName 以外の変数をまとめて扱える TypeScript
  31. オブジェクトの分割代入から Python アンパックへの適応 const users: User[] = [ { firstName:

    'Yamada', lastName: 'Taro', age: 30 }, { firstName: 'Suzuki', lastName: 'Jiro', age: 25 }, { firstName: 'Ssasaki', lastName: 'Saburo', age: 20 }, ] const [user1, user2, user3] = users # user1は{ firstName: 'Yamada', lastName: 'Taro', age: 30 } 配列も分割代入可能 TypeScript の配列の分割代入 TypeScript
  32. オブジェクトの分割代入から Python アンパックへの適応 user = ("Yamada", "Taro", "38") last_name, first_name,

    age = user print(last_name) # Yamada print(first_name) # Taro print(age) # 38 変数に入れると値が取れる Python でのタプルのアンパック Python
  33. オブジェクトの分割代入から Python アンパックへの適応 user = { "first_name": 'Yamada', "last_name": 'Taro',

    "age": 38 } user1, user2, user3 = user print(user1) # first_name print(user2) # last_name print(user3) # age アンパック可能だが取れるのはキー Python での辞書のアンパック Python
  34. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  35. 文字列のテンプレートリテラルから f-string への感動 テンプレートリテラル のプレースホルダーに は、文字列と数値の代入ができる。 関数などの戻り値も同じである。 改行文字 (\n)がなくても改行可能 interface

    User { firstName: string lastName: string age: number } const user: User = { firstName: '山田', lastName: '太郎', age: 30, } const now = new Date() const helloPyCon = ` こんにちは${user.firstName}${user.lastName}(${user.age})さん。 本日はPyCon(${now.toLocaleDateString('ja-JP')})ですね。 ` こんにちは山田太郎(30)さん。 本日はPyCon(2025/9/26)ですね。 TypeScript
  36. 文字列のテンプレートリテラルから f-string への感動 from datetime import datetime now = datetime.now()

    user = { "firstName": "山田", "lastName": "太郎", "age": 30, } hello_pycon = f""" こんにちは{user['firstName']}{user['lastName']}({user['age']})さん。 本日はPyCon({now:%Y/%m/%d})ですね。 """ こんにちは山田太郎(30)さん。 本日はPyCon(2025/9/26)ですね。 Python f-string はテンプレートリテラルでできること は殆どできます。 複数行を書く時はクォーテーションを 3つで 囲む必要がある。
  37. 文字列のテンプレートリテラルから f-string への感動 user = { "firstName": "山田", "lastName": "太郎",

    "age": 20, } print(f"{user['firstName']=}, {user['age']=}") # user['firstName']='山田', user['age']=20 変数の前に = を入れると 値が取れる Python
  38. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. JS

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  39. camelCase から snake_case へ:命名規則の文化的違い TypeScript には TypeScript の Python には

    Python の 尊重すべき文化があるので、お互いに尊重しよう!
  40. camelCase から snake_case へ:命名規則の文化的違い 命名規則に気をつけてますか? Python は、公式の PEP8 に細かくコーディング規約が記載されいます。 基本的にはそちらに準拠するのが望ましい。

    TypeScript には PEP8 のような公式のコミュニティはない。 Google や Airbnb から非公式なガイドラインがでている。 これらを参考に命名規則をチームで決めのが良い!
  41. camelCase から snake_case へ:命名規則の文化的違い 命名規則に気をつけてますか? Python は、公式の PEP8 に細かくコーディング規約が記載されいます。 基本的にはそちらに準拠するのが望ましい。

    TypeScript には PEP8 のような公式のコミュニティはない。 Google や Airbnb から非公式なガイドラインがでている。 これらを参考に命名規則をチームで決めのが良い!
  42. camelCase から snake_case へ:命名規則の文化的違い このようにお互いの 文化を守り尊重することができます。 例えば CSS のクラス名1つとっても snake_case

    にするか kebab-case する のかで思想が違う! 言語が違えば尚更なので、お互いの文化圏を守りましょう!
  43. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  44. array.map() からリスト内包表記へ: Python らしい書き方の発見 interface Variant { id: number label:

    string value: string } interface Label { id: number name: string } const variants: Variant[] = [ { id: 1, label: '赤', value: 'Red' }, { id: 2, label: '緑', value: 'Green' }, ] const convertLabels: Label[] = variants.map((variant) => { if (variant.value === 'Red') { return { id: variant.id, name: '真紅' } } return { id: variant.id, name: variant.label } }) variants を map メソッドを利用して、 Label 型の配列に 変換しております。条件があっても比較的に読みやす いです。 TypeScript
  45. array.map() からリスト内包表記へ: Python らしい書き方の発見 interface Variant { id: number label:

    string value: string } interface Label { id: number name: string } const variants: Variant[] = [ { id: 1, label: '赤', value: 'Red' }, { id: 2, label: '緑', value: 'Green' }, ] const convertLabels: Label[] = variants .filter((variant) => variant.value === 'Red') .map((variant) => ({ id: variant.id, name: '真紅' })) 配列メソッドには他にも filter など便利なのが多い TypeScript
  46. array.map() からリスト内包表記へ: Python らしい書き方の発見 class Variant: def __init__(self, id: int,

    label: str, value: str): self.id = id self.label = label self.value = value class Label: def __init__(self, id: int, name: str): self.id = id self.name = name variants: list[Variant] = [ Variant(id=1, label="赤", value="Red"), Variant(id=2, label="緑", value="Green"), ] convert_labels: list[Label] = [ Label(id=variant.id, name=variant.label) for variant in variants ] Python Python Python のリスト内包表記 非常にシンプルで美しい
  47. array.map() からリスト内包表記へ: Python らしい書き方の発見 convert_labels: list[Label] = [ Label(id=variant.id, name="真紅")

    if variant.value == "Red" else Label(id=variant.id, name=variant.label) for variant in variants ] 条件が複雑になると一気に可読性が落ちる Python convert_labels: list[Label] = [ Label(id=variant.id, name=variant.label) for variant in variants ] 非常にシンプルで美しい Python のリスト内包表記
  48. array.map() からリスト内包表記へ: Python らしい書き方の発見 users = [ {"name": "さくら", "age":

    19}, {"name": "たろう", "age": 22}, {"name": "はなこ", "age": 30}, ] adult_users = [ print(user["name"]) for user in users if user["age"] >= 20 ] Python Python のリスト内包表記
  49. array.map() からリスト内包表記へ: Python らしい書き方の発見 users = [ {"name": "さくら", "age":

    19}, {"name": "たろう", "age": 22}, {"name": "はなこ", "age": 30}, ] adult_users = [ print(user["name"]) for user in users if user["age"] >= 20 ] 20歳以上のユーザーリストを作成してるように見 えるが、 print() が埋め込まれていても気付きづら い... 予期せぬことが起こり得る可能性があるので、 このような場合、通常の for 文にする方が良い場 合も多い。 副作用に気を付ける Python Python のリスト内包表記
  50. 2. 言語の違いから学ぶ、たくさんのカルチャーショック 1. TypeScript の型システムで得ていた安心感 2. Python の動的型付けへの最初の戸惑い 3. TypeScript

    の == と === の違いと、Python の == と is の理解 4. undefined と null から None へ:「無」の概念の統一 5. オブジェクトの分割代入から Python アンパックへの適応 6. 文字列のテンプレートリテラルから f-string への感動 7. camelCase から snake_case へ:命名規則の文化的違い 8. array.map() からリスト内包表記へ: Python らしい書き方の発見
  51. 2. 言語の違いから学ぶ、たくさんのカルチャーショック まとめ コンパイルエラー 型安全なダックタイピング === undefined と null 分割代入

    テンプレートリテラル camelCase array.map ランタイムエラー 型安全なダックタイピング == と is None アンパック f-string snake_case リスト内包表記 いろいろ比べてきたが ...
  52. 2. 言語の違いから学ぶ、たくさんのカルチャーショック まとめ コンパイルエラー 型安全なダックタイピング === undefined と null 分割代入

    テンプレートリテラル camelCase array.map ランタイムエラー 型安全なダックタイピング == と is None アンパック f-string snake_case リスト内包表記 結論どちらの言語も 好 きです!
  53. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 Python にも mypy や uv

    などを導入することで開発環境が劇的によくなります。 チーム開発でも個人開発でもこの機会にでも是非導入することをオススメします。 開発効率が向上します。 TypeScript との対比表を元におすすめのものを記載します。
  54. TypeScript Python npm uv nvm uv tsconfig mypy / pyright

    eslint ruff prettier ruff / black TypeScript ライクな開発体験の構築 3. 実践!TypeScript エンジニアからの Python 型安全性戦略
  55. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 商品名: おしゃれなTシャツ 価格: 3,333円 税率:

    10% 数量: 白 - 1000個 / 黒 - 5555個 / 赤 - 7777個 バリエーション: 色(白・黒・赤) ※赤のみセール品 3% OFF 出力内容: セール品と非セール品の小計を表示する。 全ての商品の合計金額を表示する。 シナリオ
  56. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 export interface Variant { key:

    string value: string type?: string | null } export interface Product { name: string price: number tax: number quantity: number variant: Variant } TypeScript @dataclass(frozen=True) class Variant: key: str value: str type: str | None = None @dataclass(frozen=True) class Product: name: str price: Decimal tax: Decimal quantity: int variant: Variant Python
  57. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 const products: Product[] = [

    { name: 'おしゃれなTシャツ', price: 3333, tax: 0.1, quantity: 1000, variant: { key: 'color', value: '白' }, }, { name: 'おしゃれなTシャツ', price: 3333, tax: 0.1, quantity: 5555, variant: { key: 'color', value: '黒' }, }, { name: 'おしゃれなTシャツ', price: 3333, tax: 0.1, quantity: 7777, variant: { key: 'color', value: '赤', type: 'セール' }, }, ] TypeScript products: list[Product] = [ Product( "おしゃれなTシャツ", Decimal("3333"), Decimal("0.1"), 1000, Variant("color", "白") ), Product( "おしゃれなTシャツ", Decimal("3333"), Decimal("0.1"), 5555, Variant("color", "黒") ), Product( "おしゃれなTシャツ", Decimal("3333"), Decimal("0.1"), 7777, Variant("color", "赤", "セール") ), ] Python
  58. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 // セール品と非セール品の小計 const saleSubtotal =

    products .filter((p) => p.variant.type === 'セール') .map((p) => p.price * (1 + p.tax) * 0.97 * p.quantity) .reduce((acc, subtotal) => acc + subtotal, 0) const normalSubtotal = products .filter((p) => p.variant.type === undefined || p.variant.type === null) .map((p) => p.price * (1 + p.tax) * p.quantity) .reduce((acc, subtotal) => acc + subtotal, 0) // 合計 const total = saleSubtotal + normalSubtotal TypeScript
  59. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 // セール品と非セール品の小計 const saleSubtotal =

    products .filter((p) => p.variant.type === 'セール') .map((p) => p.price * (1 + p.tax) * 0.97 * p.quantity) .reduce((acc, subtotal) => acc + subtotal, 0) const normalSubtotal = products .filter((p) => p.variant.type === undefined || p.variant.type === null) .map((p) => p.price * (1 + p.tax) * p.quantity) .reduce((acc, subtotal) => acc + subtotal, 0) // 合計 const total = saleSubtotal + normalSubtotal TypeScript 配列のメソッドが多彩で優秀
  60. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 // セール品と非セール品の小計 const saleSubtotal =

    products .filter((p) => p.variant.type === 'セール') .map((p) => p.price * (1 + p.tax) * 0.97 * p.quantity) .reduce((acc, subtotal) => acc + subtotal, 0) const normalSubtotal = products .filter((p) => p.variant.type === undefined || p.variant.type === null) .map((p) => p.price * (1 + p.tax) * p.quantity) .reduce((acc, subtotal) => acc + subtotal, 0) // 合計 const total = saleSubtotal + normalSubtotal TypeScript 配列のメソッドが多彩で優秀 undefined と null のケアが大変
  61. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 # セール品と非セール品の小計 sale_subtotals = [

    (p.price * (1 + p.tax) * Decimal("0.97") * p.quantity).quantize(Decimal("1"), rounding=ROUND_HALF_UP) for p in products if p.variant.type == "セール" ] normal_subtotals = [ (p.price * (1 + p.tax) * p.quantity).quantize(Decimal("1"), rounding=ROUND_HALF_UP) for p in products if p.variant.type is None ] # 合計 total = sum(sale_subtotals) + sum(normal_subtotals) Python
  62. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 # セール品と非セール品の小計 sale_subtotals = [

    (p.price * (1 + p.tax) * Decimal("0.97") * p.quantity).quantize(Decimal("1"), rounding=ROUND_HALF_UP) for p in products if p.variant.type == "セール" ] normal_subtotals = [ (p.price * (1 + p.tax) * p.quantity).quantize(Decimal("1"), rounding=ROUND_HALF_UP) for p in products if p.variant.type is None ] # 合計 total = sum(sale_subtotals) + sum(normal_subtotals) Python リスト内包表記
  63. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 # セール品と非セール品の小計 sale_subtotals = [

    (p.price * (1 + p.tax) * Decimal("0.97") * p.quantity).quantize(Decimal("1"), rounding=ROUND_HALF_UP) for p in products if p.variant.type == "セール" ] normal_subtotals = [ (p.price * (1 + p.tax) * p.quantity).quantize(Decimal("1"), rounding=ROUND_HALF_UP) for p in products if p.variant.type is None ] # 合計 total = sum(sale_subtotals) + sum(normal_subtotals) Python None のケア1つだから簡単 リスト内包表記
  64. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 // 出力 `セール品小計: ${Math.round(saleSubtotal).toLocaleString()}円` `セール品以外小計:

    ${Math.round(normalSubtotal).toLocaleString()}円` `合計: ${Math.round(total).toLocaleString()}円` TypeScript セール品小計 : 27,657,431円 セール品以外小計 : 24,032,597円 合計: 51,690,027円 toLocaleString()を使用して、カンマ表示
  65. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 # 出力 f"セール品小計: {sale_subtotal:,}円" f"セール品以外小計:

    {normal_subtotal:,}円" f"合計: {total:,}円" Python セール品小計 : 27,657,431円 セール品以外小計 : 24,032,597円 合計: 51,690,028円 f-string だとカンマ表示が簡単
  66. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 Python セール品小計: 27,657,431円 セール品以外小計: 24,032,597円

    合計: 51,690,028円 TypeScript セール品小計: 27,657,431円 セール品以外小計: 24,032,597円 合計: 51,690,027円 金額が違う!
  67. 3. 実践!TypeScript エンジニアからの Python 型安全性戦略 TypeScript の number は 64

    ビット浮動小数点 で表現されている。 これは整数と小数が同じ型だからで、 小数点以下が二進法で正確に表せない数字があるため! 金融の計算は Python の方が得意!
  68. 参考資料 用語 リンク mypy https://mypy.readthedocs.io/en/stable/ f-string https://note.nkmk.me/python-f-strings/ PEP8 https://pep8-ja.readthedocs.io/ja/latest/ uv

    https://github.com/astral-sh/uv Airbnb JavaScript Style Guide https://github.com/airbnb/javascript Google Java Style Guide https://google.github.io/styleguide/javaguide.html