TypeScriptはデータ構造を隠蔽する機能が弱い
(classを使えという話ではある)
classを使わずにどうにかする方法を考えたい
入れ子のデータ構造を作ったときに内部が見えてしまう
こんな感じのネストした構造があった時、
code:ts
type C = { b: B };
type B = { a: A };
type A = { value: number };
Cに依存しているものは、c.b.a.valueとすれば内部が見えてしまう
aやvalueを隠蔽することができない
全ての子の構造を知ってるのと同じ
内部のデータ構造を変更したときに、c.b.a.valueのように参照している箇所に影響がある
interfaceが広すぎる、カプセル化が全然できていない あらゆるデータをgetter関数経由で取得する様に強制しない限り防げないと思うmrsekut.icon
で、あらゆるデータをgetter関数経由で取得させるのは、あまり現実的でない
linterがあれば強制できるが、たぶん運用がめっちゃ面倒になる
unionされた型は、共通したpropertyにはアクセスできない
code:ts
type X = A | B | C;
let x: X;
const _ = x.a // error
type A = { kind: 'a'; a: boolean };
type B = { kind: 'b'; b: number };
type C = { kind: 'c'; c: string };
しかしこれもif等で条件分岐すれば、その後に全部見えてしまう
対策
単純にproperty名をアンダーバーで始めて「使っちゃダメよ」感を出す
運用でカバー
Zodも_defみたいに_から始まるpropertyがかなりある
code:ts
const c: C = { b: { _a: { _value: 1 } } };
外部に公開する用の型を別途用意する
code:ts
export type PubC = { b: unknown };
type PriC = { b: B };
type B = { a: A };
type A = { value: number };
code:use.ts
const c1: PriC = { b: { a: { value: 1 } } };
const c2: PubC = c1
const u1 = c1.b.a.value;
const u2 = c2.b.a.value; // 'c2.b' is of type 'unknown'.
しかしこれ機能しないな
module内関数の定義してもinterfaceとの不一致でエラーになる
code:ts
// module内関数
export const getValue = (c: PubC) => {
return c.b.a.value; // エラーになっちゃう
};
例えば、PubCをnewtypeとして定義して、
PubCということはPriCである、という風にするなら使えそう