かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

TypeScript Readonly と as const

TypeScript なんもわからん…
Readonly と as const がよく判らなかったから調べたメモ

Readonly

TypeScriptの型システムでは、インターフェース上の個々のプロパティをreadonlyとしてマークすることができます。これにより、関数的に作業することができます(予期しない変更は良くありません)。
cf. Readonly - TypeScript Deep Dive 日本語版

オブジェクトのプロパティを変更不可にするイメージ?

type Idol = Readonly<{
  name: string;
  type: 'cute' | 'cool' | 'pop' | 'sexy';
  school?: string;
  brand?: string;
  songs: {
      [key: string]: string[];
  };
}>;

const Hoshimiya: Idol = {
  name: '星宮いちご',
  type: 'cute',
  school: 'スターライト学園',
  brand: 'Angely Sugar',
  songs: {
      cute: ['アイドル活動!'],
      cool: ['Signalize!']
  },
};

Hoshimiya.type = 'cool';
// => Cannot assign to 'type' because it is a read-only property.

Hoshimiya.songs.cute.push('輝きのエチュード');
// 変更できる

readonly な型が与えられてるオブジェクト直下のプロパティは変更しようとするとエラーになるけど、Objectや配列の中身は変更できるっぽい。

as const ( const assertion )

const Hoshimiya = {
  name: '星宮いちご',
  type: 'cute',
  school: 'スターライト学園',
  brand: 'Angely Sugar',
  songs: {
      cute: ['アイドル活動!'],
      cool: ['Signalize!']
  },
} as const;

Hoshimiya.type = 'cool';
// => Cannot assign to 'type' because it is a read-only property.

Hoshimiya.songs.cute.push('輝きのエチュード');
// => Property 'push' does not exist on type 'readonly ["アイドル活動!"]'.

// スプレッド演算子で展開して別オブジェクトするのはOK
{...Hoshimiya, name: '霧矢あおい', type: 'cool'};
//=> {name: '霧矢あおい', type: 'cool', ...};

as const だと、ネストしているオブジェクト内にも readonly が与えられて変更を加えようとしてもエラーになるっぽい。
(この構造だと引退したアイドルにしか使えなさそう…)

学校も曲も変更できないとか引退したアイドル図鑑作るとかでしか使えなさそうな型なので、こんな感じ?

type Idol = {
  readonly name: string;
  type: 'cute' | 'cool' | 'pop' | 'sexy';
  school?: string;
  brand?: string;
  songs?: {
      [key: string]: string[];
  };
}

Readonly でも as const でも別のオブジェクトに代入して変更することができてしまう

type IdolBase = Readonly<{
  name: string;
}>;

const Hoshimiya: IdolBase = {
  name: '星宮いちご',
};

const Shibuki = Hoshimiya;
// 代入はエラーにならない
Shibuki.name = '紫吹蘭';
// => Cannot assign to 'name' because it is a read-only property.

const Kiriya: { name: string } = Hoshimiya;
Kiriya.name = '霧矢あおい';
// 別途型を指定していると変更できてしまう
console.log(Hoshimiya);
// => { "name": "霧矢あおい" }

const Otome: any = Hoshimiya;
// any 型に代入すると何でも変更できる
Otome.name = '有栖川おとめ';
Otome.unit = 'ぽわプリ〜'

console.log(Hoshimiya);
// => { "name": "有栖川おとめ", "unit": "ぽわプリ〜" } 
console.log(Hoshimiya === Kiriya)
// true

Object.freeze を使うと実行時にエラーにすることができる

const Hoshimiya: IdolBase = Object.freeze({
  name: '星宮いちご',
});

const Kiriya: { name: string } = Hoshimiya;
Kiriya.name = '霧矢あおい';
// => TypeError: Cannot assign to read only property 'name' of object '#<Object>'

感想

ざっくり Readonly も as const もオブジェクトの変更をエラーで教えてくれるようにするもので、as const はネストしている配列やオブジェクトにも再帰的に readonly を指定してくれる。
ただし、オブジェクトを型が異なる別のオブジェクトに代入してしまうと、参照元のオブジェクトのプロパティを変更できてしまうので注意が必要。
関数型的な考え方をしていればオブジェクトをそのまま他のオブジェクトに代入する事自体がキモチワルイ感じだけど、防げないのでガチガチに固めるなら Object.freeze を使うのが良さそう。


[参考]

プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発

プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発

  • 作者:Boris Cherny
  • 発売日: 2020/03/16
  • メディア: 単行本(ソフトカバー)

毎週火曜日アイカツ!をみましょう!!!

Docker AWS DynamoDB local 環境作った

サーバレスの Lambda 入門しました。

この本によると AWS の Lambda は実行ごとにコンテナが立ち上がってDBにコネクションを張るので、RDBは相性が良くなく、Key-Value Store の DyanamoDB の方が良いらしく DyanamoDB にも入門。

AWS のドキュメント にはプログラムを Download して Java で実行させる方法や Docker で実行させる方法のドキュメントが有りました。本のサンプルは Java で実行させる方法だったのですが、折角なので Docker で作成してみたのでメモ。

DynamoDB を Docker で使う公式のドキュメント

公式にある docker-compose のコードはこんな感じ (このまま使える、ありがたい!

version: '3.7'
services:
 dynamodb-local:
   image: amazon/dynamodb-local:latest
   container_name: dynamodb-local
   ports:
    - "8000:8000"

このまま実行したら DynamoDB を使うことができたのですが、デフォルトの設定では -inMemory で起動されるようでデータが永続化されていないのでコンテナを落とすとデータが消えてしまいました。

データを永続化させる

volumes でデータをローカル環境に作成するようにすればOK

構成

|- docker-compose.yml
|- /dynamoDB
     |- /data # ここにデータを永続化させたい

docker-compose.yml

version: '3.7'
volumes:
  dynamodb:
    driver: local
services:
  dynamodb-local:
    image: amazon/dynamodb-local:latest
    container_name: dynamodb-local
    volumes:
      - ./dynamoDB/data:/home/dynamodblocal/data
    ports:
      # API で 8000 番ポートを使いたかったので 8888 に変更
      - "8888:8000"
    command:
      -jar DynamoDBLocal.jar -sharedDb -dbPath ./data -optimizeDbBeforeStartup

これで $ docker-compose up をしてビルドを行うと localhost:8888 で DynamoDB にアクセスすることが出来るようになりまし、docker のコンテナを落としてもデータが消えることなく開発の続きを行えるようになりました! ( ᐢ˙꒳​˙ᐢ ) わーい

command の説明

  • -jar DynamoDBLocal.jar … DynamoDB の起動コマンド。(コンテナをビルドすればコンテナ内の /home/dynamodblocal 内に DynamoDBLocal.jar が作成されている)
  • -sharedDb … 認証情報(AWS_ACCESS_KEY_ID)・リージョンが異なっていてもデータベースファイルを分けず共通データベースファイルを使用する。

    -sharedDb — If you specify -sharedDb, DynamoDB uses a single database file instead of separate files for each credential and Region.

  • -dbPath <dir name> … データベースを書き込むディレクト

    -dbPath value — The directory where DynamoDB writes its database file. If you don't specify this option, the file is written to the current directory. You can't specify both -dbPath and -inMemory at once.

  • -optimizeDbBeforeStartup … DynamoDBを起動する前にデーブルを最適化する。(-dbPath オプションが必須)

    -optimizeDbBeforeStartup — Optimizes the underlying database tables before starting DynamoDB on your computer. You also must specify -dbPath when you use this parameter.

 
サーバーレス用の DB みたいなので、endpoint でデータベースにアクセスしたり不思議な感じです。そして MongoDB や firebase fireStore みたいな感じだと思うのですが、データベースの設計が難しいみたいなのでやってみてハマりどころがあればまたメモしていきたいです。 (AWSのドキュメント日本語もあるけど、僕の頭が悪すぎるのか理解が難しい… ガンバロ…)


[参考]

あとでみる

Svelte と MobX チョット気になっている

Python zsh で仮想環境 venv に入れない?にハマる

Python の venv で作成した仮想環境に入れない問題にハマったのでメモ

venv

Python v3.3 から標準機能として取り入れられた仮想環境
venv 内では pip でインストールされるパッケージが仮想環境ごと別にすることができる

仮想環境の作成

$ python -m venv testvenv

testvenv というディレクトリに仮想環境が作成される

仮想環境の実行

仮想環境内の bin/activate を実行すると仮想環境に入れる

$ source testvenv/bin/activate
(testvenv) 
# 仮想環境から出る
(testvenv) deactivate

と、簡単に仮想環境が作成できて実行できる筈だった…

zsh で仮想環境が実行されない?

環境

zsh で venv 環境を作成して実行しようとしても何も起こらなかった

$ python -m venv testvenv
$ source testvenv/bin/activate
$

zsh の見たをカスタマイズしているのが問題だった

実際には仮想環境は実行されていたが、zsh をカスタマイズしていたために、仮想環境に入っていることの表示がされていないだけでした。
Powerlevel9k でカスマイズしていたので、表示に virtualenv を加えると仮想環境に入っていることが表示されるようになりました。

# .zprofile
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(ssh dir vcs newline virtualenv status)

👇

$ python -m venv testvenv
$ source testvenv/bin/activate
(testvenv) $

自分のしていたカスタマイズが原因だったので、原因が解るまで時間がかかってしまった…
zsh のカスタマイズパッケージの Powerlevel9k も Powerlevel10k を使うように言われているのですね…

それにしても Python の仮想実行環境簡単に作れるので驚きました


[参考]