2021年7月11日日曜日

TypeScriptで、中身が同じ別の型を区別する

タイトルを見て「ああ、アレのことね」と思った方。はい、アレです。というかURLでネタバレしてます。

アレを知っている人にとっては別に目新しいネタではないと思いますが、先日教えてもらったのでメモとして残しておきます。

中身が同じ別の型は区別できない

Go等とは異なり、TypeScriptの言語仕様として、中身が同じ別の型は区別がつきません。例えばこんなコード。

interface Foo {
    a: number;
    b: string;
}

// 中身はFooと同じ
interface Bar {
    a: number;
    b: string;
}

const a: Foo = {a: 0, b: ""};
const b: Bar = a; // 代入できてしまう

// Foo型がほしい
function getFoo(): Foo {
    const bar: Bar = {a: 0, b: ""};
    return bar; // Bar型を返してもエラーにならない
}

実際にコンパイラーを通した結果はこちら

現時点でたまたま中身が同じだけど本質的に意味が異なるようなデータ(一般ユーザーとシステム管理者のようなやつ)は厳密に区別したいですよね。

DDDを導入したい場合なんかも厳密に区別したいです。というより状況を問わず厳密に区別したいです

幽霊型っぽいやつを使う

他の言語では、幽霊型(Phantom Type)というものがあったりします。TypeScriptでもnever型を使うと、幽霊型っぽいものを作れます。

interface Foo {
    __Foo: never; // これがミソ
    a: number;
    b: string;
}

// 中身はFooと同じ
interface Bar {
    __Bar: never; // これがミソ
    a: number;
    b: string;
}

const a: Foo = {a: 0, b: ""} as Foo;
const b: Bar = a; // 代入できない

// Foo型がほしい
function getFoo(): Foo {
    const bar: Bar = {a: 0, b: ""} as Bar;
    return bar; // Bar型を返すとエラーになる
}

コンパイラーを通すと型エラーになっているのがわかりますね。言われてみると単純な話ですが、なるほどという感じです。

  • 型にわざわざnever型をつける必要がある
  • as FooをつけないとFoo型として認識されない

このあたりがイケてないですね。いつかTypeScriptに言語仕様として取り込まれてくれないかな。例えばstrict interfaceとかstrict typeみたいに型を定義すると厳密に区別されるとか。このアイデア、パクっていいですよ>Microsoftの方々

これを教えてくれたのも前回と同じインターン生です。感謝感謝。

0 件のコメント:

コメントを投稿