かもメモ

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

TypeScript Object を Object.keys() を使ったループの型エラーにハマる

TypeScript で Object.keys(obj) でキーの配列を作ってループさせてループ内でキーでオブジェクトのデータにアクセスしようとして型エラーになってしまったのメモ

e.g.

例としてデータ構造が良くないけど、ID をキーにしたオブジェクトみたいなイメージで

const idols = {
  hosimiya: {name: '星宮いちご', type: 'cute', brand: 'Angely Sugar'},
  kirija: {name: '霧矢あおい', type: 'cool', brand: 'FUTURING GIRL'},
  shibuki: {name: '紫吹蘭', type: 'sexy', brand: 'SPICY AGEHA'},
};

Object.keys(idols).map((key) => {
  const data = idols[key];
  // => Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ hosimiya: …
  // => No index signature with a parameter of type 'string' was found on type '{ hosimiya: …
  console.log(data);
});
  • Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ hosimiya: …
  • No index signature with a parameter of type 'string' was found on type '{ hosimiya: …

オブジェクトに [key] でアクセスすると暗黙的に型変換された keyidols オブジェクトのインデックスと一致しないということっぽい。
(ᐡ •̥ ̫ •̥ ᐡ) なんもわからん…

Object.keys で作られる配列がオブジェクトのキーの配列だと証明できればOK

keyof を使うとプロパティ名を | でつないだ型 ( プロパティ名の直和型 ) が作れる

interface Soleil {
  'hosimiya': { name: string, type: string, brand: 'string' };
  'kirija': { name: string, type: string, brand: 'string' };
  'shibuki': { name: string, type: string, brand: 'string' }; 
}

type SoleilKey = keyof Soleil;
// => type SoleilKey = 'hosimiya' | 'kirija' | 'shibuki';

Object.keys(idols)SoleilKey[] になっていれば、map に渡される引数は SoleilKey である。ということになるっぽい。
修正するとこんな感じ。

interface idolData {
  name: string;
  type: string;
  brand: string;
}

interface Soleil {
  hosimiya: idolData;
  kirija: idolData;
  shibuki: idolData;
}

const idols: Soleil = {
  hosimiya: {name: '星宮いちご', type: 'cute', brand: 'Angely Sugar'},
  kirija: {name: '霧矢あおい', type: 'cool', brand: 'FUTURING GIRL'},
  shibuki: {name: '紫吹蘭', type: 'sexy', brand: 'SPICY AGEHA'},
};

(Object.keys(idols) as (keyof Soleil)[]).map((key) => {
  const data = idols[key]; // 👌👌👌
  console.log(data);
});

₍ ᐢ. ̫ .ᐢ ₎👌 できた

おまけ

今回のレベルだと [key: string] で型定義してしまえば、map の所でゴニョゴニョしなくても大丈夫っぽい

interface idolData {
  name: string;
  type: string;
  brand: string;
}

interface Idols {
  [key: string]: idolData;
}

const idols: Idols = {
  kurebayashi: {name: '紅林珠璃', type: 'sexy', brand: 'Sangria Rosa'},
  Amahane: {name: '天羽まどか', type: 'cute', brand: 'Angely Sugar'},
  Kurosawa: {name: '黒沢凛', type: 'cool', brand: 'Dance Fusion'},
};

Object.keys(idols).map((key) => {
  const data = idols[key]; // 👌👌👌
  console.log(data);
});

TypeScript チョットづつ慣れてきてるけどまだ何もわからん…


[参考]

速習 TypeScript 第2版 速習シリーズ

速習 TypeScript 第2版 速習シリーズ

React localhost でも https で動かしたい

create-react-app, react-scripts で作った React アプリを外部ライブラリやAPIの都合で localhost の時も https で動かしたい時のメモ

.env に HTTPS=true を設定すればOK

HTTPS=true のオプション付きで起動すれば localhosthttps で起動できるようです。
create-react-app, react-scripts で作成した react アプリは自動的に .env ファイルを見るようになっているので、ここに HTTPS=true を設定すればOK

# .env
HTTPS=true

この設定をして yarn start すれば自動的に https://localhost:3000 で起動する。 ヤッタネ ₍ ᐢ. ̫ .ᐢ ₎👌


[参考]

Long time no sea[ 初回盤 / ボーナスディスク付き ]

Long time no sea[ 初回盤 / ボーナスディスク付き ]

さよポニの みぃな のアルバムめちゃ良い〜

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
  • メディア: 単行本(ソフトカバー)

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