TypeScriptのライブラリのinterfaceを拡張する
TypeScript + Nextで開発中、scriptタグを使う必要が出てきた。
(多分)バージョン11から import Script from 'next/script'
を使ってScriptコンポーネント 使うようにしないと怒られる。
そこで <Script />
を使ったのだが、propsに渡す型でエラーでた。
import Script from 'next/script' <Script foo="foo", bar="bar" /> // Property 'foo' does not exist on type 'IntrinsicAttributes & ScriptProps'.
まあScriptPropsにfooもbarも定義されていないので当然。
なのでScriptPropsを拡張する必要があった。
アンビエントモジュールの定義
ここではモジュールの型を変える必要があるのでモジュールそのものの型定義(script.d.ts)ファイルを用意する。
これは declare module 'モジュール名'
で作れる。
import Script from 'next/script' declare module 'next/script' { interface ScriptProps { foo: string bar: string } } export default Script
TypeScriptのコンパイラが自動でこのファイルが読み込まれ、next/script
の型はこのアンビエントモジュールが優先されて使用されるようになる。
interface ScriptProps
により既に定義されているScriptPropsに新しい定義をマージ。これでfoo, barがTSで使用できるようになった。
TypeScriptのtypeとinterfaceの違う部分を少し見る
typeはエイリアスであり、いろんな型を表すことができる
type A = number type B = number | string type C = { name: string age: number }
interfaceはnumberとかnumber | stringのエイリアスは作れない
type A = number // error type B = number | string // error interface C { // ok name: string age: number }
typeはオブジェクト型の交差には &
を使用する
type A = { name: string; }; type B = { age: number; }; type Profile = A & B; const profile: Profile = { name: 'name', age: 23, };
interfaceはextendsで拡張する
interface A { name: string; } interface Profile extends A { age: number; } const profile: Profile = { name: 'name', age: 23, };
interfaceは宣言のマージができる。
マージされたすべてのプロパティに従わなければいけない
interface Profile { name: string; age: number; } interface Profile { sex: string; } const user: Profile = { name: 'name', age: 23, sex: 'men', };
typeは宣言のマージはできない
// error type Profile = { name: string; age: number; }; type Profile = { sex: string; };
PrismaのschemaでHence the relation field must be optional as well.が出た
Userは1つのGroupに所属し、Groupは複数のUserを持つので "one to many" の関係性である。
Userは必ずしもGroupに所属しているとは限らない。
↓のようなshemaができた。
model User { groupId Int? group Group @relation(fields: [groupId], references: [id]) } model Group { id Int @id @default(autoincrement()) members User[] }
これだと
"Error validating: The relation field group
uses the scalar fields groupId. All those fields are optional. Hence the relation field must be optional as well."
というエラーが出る。
グループに所属していない可能性もあるので、groupIdはoptionalである。
にもかかわらず リレーションフィールドのgroupがoptionalになっていなかったが原因だった。
↓にすればエラーが消える
group Group? @relation(fields: [groupId], references: [id])
forkしたライブラリの特定のブランチを使いたい
解決策
末尾に #<ブランチ名> を追加
"git+{CodeのHTTPSからコピったURL}#<ブランチ名>"
ストーリー
{ "dependencies": { "react-native-elements": "git+{CodeのHTTPSからコピったURL}" } }
こんな感じでライブラリを使っていたが特定のブランチを使いたかった。
その場合は
{ "dependencies": { "react-native-elements": "git+{CodeのHTTPSからコピったURL}#<ブランチ名>" } }
これでいけた
forkしたライブラリを使う時にdistがない
解決策
.gitignoreからdist外して、コミットする前にローカルでビルドしてdistを作成する
概略
TypeScropt製ライブラリをforkして変更加えてそれを使いたかった。
{ "dependencies": { "react-native-elements": "git+{CodeのHTTPSからコピったURL} } }
こんな感じ
本来、npm publishとかするときにビルドされてdistが作成されることが多いが、forkしたものを参照するとこのプロセスがなくなる。 なのでdist作成されずにエラーになる。
なので少しスッキリしないやり方ではあるが、ローカルでビルドしてリモートにも反映。それをnpmで使用した。
DockerでNodeサーバー立てる時はTimeZoneに気を付ける
docker-composeでHapi製サーバーを立てたが、new Date()を使った処理でどうもうまく動かない箇所があった。 調べたらTimeZoneがUTCになっておられた。
TZ: Asia/Tokyoを指定すればok
environment: TZ: Asia/Tokyo
TypeScriptでPromise<A>のAの部分の型を取得する
UnwrapPromise というユーティリティを使えば取ることができる
type P = Promise<number>; type R = UnwrapPromise<P>; // number